@gitruck/cli 1.2.15 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -239,7 +239,7 @@ Fetches the report and the three project formats of an **already completed** tas
239
239
 
240
240
  | Parameter | Purpose | Default |
241
241
  |---|---|---|
242
- | `-o, --out <dir>` | Output folder | **Required** (no default since 2026-09-08; `--out .` = the current directory itself) |
242
+ | `-o, --out <dir>` | Output folder | **Required** (`--out .` = the current directory itself) |
243
243
  | `--render` | Additionally render locally (requires the raw file still at the path embedded in gtrk, plus ffmpeg) | off |
244
244
  | `--jianying-draft-dir <dir>` | Jianying draft root (or `auto`) | Reads the init config / auto-detects |
245
245
  | `--no-open` / `--json` | Same as `oralcut` | — |
@@ -329,7 +329,7 @@ gtrk patch set --project <dir> --track audio:1 --at 3.0 --volume 0.5
329
329
  | `--highlight-weight <w>` | `matrix lay` only: fold "is there anything worth watching" (information density / drama / emotional intensity / rarity) into candidate ranking, 0..1. **Orthogonal** to `--mark-weight` (how good the frame looks); the two weights are clamped to sum to 1. ⚠️ The highlight score reads the `describe` understanding cache, so **without a prior `describe` run this flag does nothing** — uncached candidates are treated as neutral and the weight is returned to similarity | `0` (off, zero regression) |
330
330
  | `--decode-path <mode>` | `matrix index` only: decode path for scene detection, `auto` \| `gpu` \| `cpu` \| `full`. `auto` probes for hardware decoding and **degrades per asset** (recommended); `gpu`/`cpu`/`full` pin one tier and **do not degrade on failure** (for A/B comparison and troubleshooting). ⚠️ The default is still `full` (legacy behaviour) — you have to pass `auto` yourself to get the speedup | `full` |
331
331
  | `--proxy-width <n>` | `matrix index` only: proxy decode width. ⚠️ Below this, fidelity degrades noticeably — **do not shrink it casually** | `384` |
332
- | `--proxy-scaler <name>` | `matrix index` only: proxy scaling algorithm. Defaults to `neighbor` (point sampling, no filtering; measured faster *and* more accurate than `bicubic`). ⚠️ You basically only need to change this to run a comparison experiment | `neighbor` |
332
+ | `--proxy-scaler <name>` | `matrix index` only: proxy scaling algorithm. Defaults to `neighbor` (point sampling, no filtering); pass another value when you need a different algorithm | `neighbor` |
333
333
  | `--exclude-recent <n>` | `matrix material --scope audio` only: avoid the last n BGM tracks already used. The history is **booked automatically** when `audio lay` writes to the track — you do not maintain it | `12` |
334
334
  | `--no-exclude-recent` | Turn off the BGM avoidance above, allowing recent tracks to be reused | off (avoidance is the default) |
335
335
 
@@ -348,9 +348,9 @@ gtrk patch set --project <dir> --track audio:1 --at 3.0 --volume 0.5
348
348
  >
349
349
  > **Machine-readable accounting: `counts.results` is the pre-dedup number**: in `--json`, `counts.results` is always the **sum of retrieval-response rows across queries** (unchanged legacy semantics) — when 15 queries all hit the same material it reads 15, while the landed plan may hold only 8 rows covering a single material. To judge "how much footage is actually there", read the three keys emitted in dispatch-consumption mode: `counts.zero_yield` (queries that **truly yielded nothing**, decided on the **retrieval response** being empty rather than by scanning the plan for `results: []` afterwards — in-beat dedup folds a hit into a sibling query of the same beat, and folding ≠ zero yield), `counts.plan_results` (the actual number of result rows **after the plan lands**, deduplicated) and `counts.distinct_clips` (distinct `clip_id` count inside the plan). These three appear **only in dispatch-consumption mode**; the `counts` of ad-hoc `matrix search` and `matrix lay` are byte-for-byte unchanged (**absence means "no such concept here", not "measured as 0"**). On the laying side it likewise emits `lay.beatsWithCandidates` (beats that have candidates) and `lay.emptyBeats` (**the list of zero-candidate beats** — spans with no footage to lay at all); the two always satisfy `beatsWithCandidates + emptyBeats.length = the plan's total beat count`.
350
350
  >
351
- > **Material-on-disk self-check**: after writing back the project it verifies that every `materials[].path` really is on disk (**read-only, reports without touching anything**). Relative paths are always resolved against the **directory containing the `.gtrk` file** (`<output>/gtrk/`). `--json` emits `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }` — `dangling` is the complete list of **broken references** among project-owned materials (registered but missing on disk), each flagged with **whether the timeline references it** and where (a referenced one means that span has no media to show, far worse than an orphan); missing absolute paths are counted separately as `external` (an unmounted external drive looks like this too, so it does not pollute the main verdict); http(s) materials are only counted and **no network requests are made**. **This informs, it does not block**: broken references do not change `ok`, do not change the exit code, and no material entry or file is deleted. They are usually historical residue (e.g. an interrupted "confirm source clip" download); the fix is to re-confirm the source clip in the client or delete that clip. Runs that never wrote back (`--lay 0` / refused / missing project) **emit no `integrity` field** — absence means "not checked this time", not "checked and clean".
351
+ > **Material-on-disk self-check**: after writing back the project, the CLI read-only checks whether every `materials[].path` exists. Relative paths resolve against the directory containing the `.gtrk` file (`<output>/gtrk/`). `--json` emits `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }`; `dangling` lists registered materials missing on disk and marks whether the timeline references each one, while missing absolute paths are counted as `external`; http(s) materials are counted without network requests. This check only reports: it does not change `ok`, the exit code, material entries or files. Repair a dangling reference by re-confirming the source clip in the client or deleting the corresponding clip. Runs that never write back (`--lay 0` / refused / missing project) emit no `integrity` field.
352
352
  >
353
- > **The solid black bed track**: by default a solid black track is laid beneath all candidate tracks and above the talking-head main track (`struct_meta.broll.black_track` records its `track_index`), covering the full landed beat envelope so that during B-roll (including the empty spots on candidate tracks) the talking-head picture underneath is not exposed. **The cost is "black holes"**: wherever candidate tracks are not filled, pure black covers the talking-head, and track laying computes exactly that — `--json` always emits `lay.blackBedHoleSec` plus per-span `lay.blackBedHoles`, and a non-fatal warning is added when a single span is ≥ 3 s or a single beat's ratio is ≥ 15 % (it does not change the exit code or block laying). Use it to adjust `--score-floor`, switch to `--no-black-bed`, or patch by hand in the client. The bytes land at `assets/builtin/solid-000000-<W>x<H>.png`, sharing an id namespace with the client's built-in solid material and reused idempotently. Do not delete it by accident when removing candidate tracks; to swap footage, drag onto a candidate track's clip and **not onto the black bed** — since client 0.2.10 (force-updated release on 2026-07-31) **dropping onto the black bed is rejected outright with a message**. On clients older than 0.2.10 (force update not yet pulled) the old behaviour silently creates a new video track and inserts there; if it lands in the lower half you cannot see it in the preview at all (one `Ctrl+Z` undoes the whole thing) — restart the client first to pick up the force update. If you do not want the black bed, re-run with `--no-black-bed` and it is stripped clean.
353
+ > **The solid black bed track**: by default a solid black track covers the landed beat envelope beneath candidate tracks and above the talking-head track, so empty B-roll spans do not reveal the picture underneath. Empty candidate spans become “black-bed holes”; `--json` always emits `lay.blackBedHoleSec` and `lay.blackBedHoles`, with a non-fatal warning at ≥3 s for one span or ≥15% of one beat. Adjust `--score-floor`, use `--no-black-bed`, or patch in the client. The material is stored at `assets/builtin/solid-000000-<W>x<H>.png` and reused idempotently; drag replacements onto candidate tracks, never onto the black bed. Client 0.2.10+ rejects drops onto the black bed; upgrade older clients first. Use `--no-black-bed` to remove it.
354
354
 
355
355
  **Local-footage mode (`matrix index` / `--local`)**: footage does not have to live in the cloud library — retrieve and lay tracks straight from your local footage folders (video and images mixed):
356
356
 
@@ -375,11 +375,11 @@ gtrk matrix lay --project <dir> [--plan <path>] # ③ consume the
375
375
  >
376
376
  > **Do not read `false` as "no copyright, use it freely" — it means exactly the opposite.** The decisive counter-example: concept material (other people's copyrighted works) is ingested with a hard-coded `is_copyright=0`; if the field really meant "is under copyright", that batch would have to be 1.
377
377
  >
378
- > This warning comes from a real incident: on 2026-09-02 an AI operator **deliberately picked `false` twice in a row** as the "safe" option and laid non-commercial material into 5 projects, while the only safe value, `true`, was actively avoided. So under `--json` the CLI now **derives a plain-words label `copyright_label`** per result (`"可商用"` = commercially usable / `"不可商用"` = not) — derived from `is_copyright`, always in the same direction, absent whenever `is_copyright` is absent. Read either key; never infer from the field name.
378
+ > `is_copyright` means “commercially usable”: `true` = usable and `false` = not usable. Under `--json`, each result also carries `copyright_label` (`"可商用"` / `"不可商用"`), derived in the same direction and absent when `is_copyright` is absent; read these fields directly instead of inferring from the name.
379
379
  >
380
380
  > The field exists **only on the matrix-member endpoint**: on the public endpoint its absence does not mean "not commercially usable" — the server already restricts that endpoint to commercially usable material at the source (absent = nothing to judge; the CLI reports the absence honestly and never fabricates a value).
381
381
  >
382
- > **Default scope (⟲ 2026-09-06)**: the matrix-member tier searches the **whole library by default** (`copyright_scope=all`, including non-commercial and concept material) — that breadth is exactly what membership buys, so the command never narrows it for you, and the bundled skills must not drop `is_copyright:false` candidates "just to be safe" (they do label every result honestly). Pass `--commercial-only` yourself when you need commercially usable material only.
382
+ > **Default scope**: the matrix-member tier searches the whole library by default (`copyright_scope=all`, including non-commercial and concept material). The CLI and bundled skills do not narrow it; pass `--commercial-only` when you need commercially usable material only.
383
383
 
384
384
  Orchestration recipes (pure matching / describe-then-lay / time-window / footage-first scripting / three-layer stacking) and the plan-editing contract live in the bundled skill `/gtrk-matrix`.
385
385
 
@@ -402,14 +402,14 @@ Consumes the `dispatch.mg` dispatch landed by `gtrk split`, laying html-particle
402
402
 
403
403
  - **Laying** (`gtrk mg --project <dir>`): reads `dispatch.mg` → for each beat takes the source particle from `<project>/mg/<composition_id>.html` → lints → lays it into `beat_track` and atomically writes `struct_meta.mg` back into `.gtrk` (self-produced tracks are registered idempotently in `lay_tracks`; a re-lay strips the previous self-produced items before appending, and user-added tracks are never touched). Whether a particle is a "transparent overlay" or a "full-screen bed" is decided by `opaque`, inferred from the particle HTML's root `background`. Beats with a missing HTML file or a failed lint are counted in `skipped` and do not block the rest.
404
404
  - **The stripping surface ≠ "what gets laid this run", and ≠ "every registered track"**: `--only <beat>` **strips only the matched particles** (true incremental merge) — every other already-laid particle's clip / material / registration entry is **preserved as is**, together with any manual tweaks you made in opencut (what is preserved is the existing clip itself, not a rebuild from the registration, so transparency `opaque` is not lost); those preserved entries are **not re-linted and their source HTML is not re-copied** (the project is self-contained, so deleting files under `<project>/mg/` does not matter). A full re-lay is still "strip clean, then rebuild the whole track", with **one exception**: particles that are in this dispatch but failed to land (missing HTML / failed lint / zero surviving span after re-projection) keep their previous round's clip on the track (a broken new one must not destroy the working old one); conversely, already-laid entries **no longer present in the dispatch** are still stripped (a plan change ≠ something broke). To strip every other already-laid particle too, authorize it explicitly with `--replace-all`.
405
- - **The material table does not accumulate**: materials are stripped by "**self-produced identity × zero references**" (self-produced = a `mg-`/`rrv-` prefix **or** living under the CLI-exclusive `assets/mg/` with a filename in the self-produced registry), and **it deliberately does not trust the client-rewritable `html_material` prefix** — so re-laying after editing the project in opencut still strips old materials, the `mg-` material count **always equals the number of particles on the track**, and historical duplicate/orphan entries are cleaned up along the way. **Non-self-produced materials are never touched** (`broll-*` / `ex-solid-*` / anything you added, even with zero references); self-produced materials still referenced by a surviving clip are also not stripped (it will never strip a clip into a broken reference); the html copies under `assets/mg/` on disk are never deleted.
405
+ - **The material table does not accumulate**: strip materials by “self-produced identity × zero references” (self-produced = an `mg-`/`rrv-` prefix, or a registered filename under the CLI-exclusive `assets/mg/`), without trusting the client-rewritable `html_material` prefix. The `mg-` count stays equal to the particles on the track; duplicate and orphan entries are removed. Non-self-produced materials, self-produced materials still referenced by a surviving clip, and HTML copies under `assets/mg/` are preserved.
406
406
  - **"Nothing matched" is not a clear-everything instruction**: when `--only` matches nothing, `dispatch.mg` is empty/missing, or every entry in this run was skipped, **while the track already has laid particles**, the write-back is refused (that is a signal that the dispatch or the selector is broken). Add `--replace-all` if you really mean to clear it. A first-time lay (no existing laid entries on the track) is exempt and completes normally, reporting `laid=0`.
407
407
  - **Slot windows are re-projected on the spot**: before laying and linting, each queue entry's `[track_st, track_ed]` is recomputed from "`transcript` × the current `.gtrk`", and thereafter the lint slot envelope (house rule ⑦) and the laid clip duration both follow the recomputed values (`--only` obeys the same rule; aux particles re-project against **their own** span and are not conflated with the main beat window). The timecodes in `dispatch.mg` are only a **projection-time snapshot**, used as a fallback solely when re-projection is impossible — **after editing the talking-head track you can lay directly without re-running `gtrk split`**. `--json` always emits `reprojection:{mode,degraded,reason?,drifted,max_offset,shrunk,dropped}` (including under `--lint-only`). Entries with **zero surviving span** after re-projection are skipped and counted in `skipped` (no HTML is copied and nothing is laid back from the snapshot); if re-projection is impossible (missing transcript / project not found / no talking-head material on the main track) → it degrades to the snapshot with a warning and a `--json` marker, and the exit code is unchanged; behaviour for **non-v1** projects is unchanged (the laying path's version gate exits non-zero, while `--lint-only` still produces its report). A successful lay appends this run's timecode provenance (`timecode_source` / `reprojected_at`) into `struct_meta.mg`.
408
408
  - **lint** (`gtrk mg lint <particle.html> [--dispatch <path>]`): a purely local static check of the machine-decidable subset of the house rules for a particle HTML (wrapped in `<template>`, `data-composition-id` + 1920×1080, `gsap.timeline({ paused: true })`, registration in `window.__timelines`, no `Math.random` / `Date.now`, self-contained with no relative external links, root `background` consistent with `opaque`, …); when `--dispatch` is given it also checks that the `composition_id` matches the dispatch. Any fatal item exits non-zero and reports every reason.
409
409
  - **Expected-id consistency** (`1-cid-expect`, **fatal**): the `data-composition-id` inside the HTML must equal the expected id (when laying, the dispatch entry's `composition_id`; for `mg lint`, the filename — but only when it matches the dispatch or looks like `…-B<digits>[-aux<n>]`, so a renamed copy such as `./tmp.html` is not compared). This guards against "copying `<id>.html`, renaming the file and forgetting the id inside" — laying would write a clip/material named after the file while the file registers a different `__timelines` key and fights the same-named particle over the same style scope.
410
410
  - **House rule ⑦, timeline length estimation** (`7-fill-slot` / `7-no-estimate` / `7-infinite-repeat`, **always non-fatal, never blocks laying**): when the slot envelope is known (per particle when laying; for `mg lint --dispatch` when it matches a dispatch entry) it computes a **static lower bound** on the GSAP timeline — degrading call by call, counting whatever it can parse (`duration×(repeat+1) + repeatDelay×repeat`, with `yoyo` adding no time), while calls with expression positions or non-literal durations are **skipped and not counted** (ignoring some calls still yields a valid lower bound). Estimate < envelope → warning; nothing computable at all → an explicit notice that "length could not be estimated statically, house rule ⑦ was not verified, and it must be accepted against the real engine's seek" (**never silent**: "could not compute" and "computed and passed" are distinguishable in the output); containing `repeat:-1` → a warning that "an infinite loop makes the total length Infinity, so house rule ⑦ cannot be verified statically; please use a finite repeat computed from the slot". The real criterion is always the render engine frame by frame; this item is only a reminder layer.
411
411
  - **House rule ⑧, repeated-primitive merging** (`8-primitive-merge`, **always non-fatal, never blocks laying**): identifies mergeable batches of `line` / `rect` / `path` / `polyline` / `polygon` that are "created inside a loop, or by a named factory called in a loop; landing under the same parent node; and not driven by per-element animation". Only batches whose purely numeric loop trip counts sum to **≥ 8** under the same parent are reported; when bounds involve `.length` or named constants and cannot be computed it still reports "count unknown" without constant folding; elements with per-element `gsap.set` / tweens, or used as a tween's first argument, are excluded. This item only points out "there is a batch of repeated primitives here that can be merged **losslessly**, pixel-identical after merging" — it is **not a risk verdict**: a hit does not mean the particle will reproduce a defect, and a miss does not mean it is safe. The real criterion remains sampling frames from a real render.
412
- - **Callback and seek semantics** (`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`, **always non-fatal, never blocks laying**): aligned with the same-named section of the contract (added 2026-07-26). GSAP `seek(t)` suppresses callbacks by default → tweened properties still interpolate, but DOM writes inside `onUpdate` do not run, and the failure mode is **the picture freezing at its initial state rather than going black**. The contract places the guarantee on the **engine side** (fixing a frame MUST use `seek(t,false)` / `time(t)` / `progress(p)`), so a particle **driving its picture from callbacks is a compliant style**; these three lint items are merely **sentinels**: `x-callback-driven` = callbacks write the DOM with no seek fallback anywhere (it stays silent when a fallback exists, to avoid nagging); `x-engine-api-override` = the particle overrides `tl.seek` at runtime or replaces `__timelines[…]` with a wrapper object (which would override the engine's explicit `seek(t,true)` and stops working the moment the engine switches to `time()`/`progress()`; a transitional state); `x-raf-interval` = it contains `requestAnimationFrame(` / `setInterval(` (its own clock is not driven by seek, i.e. frozen). All three MUST NOT be fatal — "driving the picture from callbacks" is not a violation.
412
+ - **Callback and seek semantics** (`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`, **always non-fatal, never blocks laying**): aligned with the same-named contract section. The engine MUST use `seek(t,false)` / `time(t)` / `progress(p)` when fixing a frame; particles may drive their picture from timeline callbacks but MUST NOT override `tl.seek` or replace `window.__timelines[…]`. These lint items are reminders only and do not require seek shims.
413
413
  - **status** (`gtrk mg status --project <dir>`): summarizes the MG pipeline — total beats in `dispatch.mg` / how many source HTML files exist / how many are laid into `.gtrk`, annotating each beat (missing HTML / produced but not laid / laid).
414
414
  - **render** (`gtrk mg render <particle.html> --duration <sec> [--out <dir>] [--yes]`): renders a single particle in the cloud, **independent of any project**, into a Jianying-readable qtrle transparent alpha MOV (the fine-cut supplement channel — when the rough cut is already exported to Jianying and one more motion graphic is needed, you can add it without going back to the client app). The chain = lint upfront (envelope = `--duration`; any fatal item is blocked locally, zero submission, zero billing) → **billing-estimate confirmation** (live price lookup; the CLI has no local HTML render engine, so the cloud billed task is the only path for standalone particles; `--yes` skips the prompt) → inline submission to the cloud → `<composition_id>.mov` landed with `task.json`/`result.json` breadcrumbs (recoverable by task_id after a crash). **Scope**: qtrle only for now (`--format webm` is explicitly refused — Jianying cannot read VP8-alpha), 1920×1080 particles only (the contract has no portrait/odd-canvas opening), and `--duration` is required. The output never enters `.gtrk` and never lands in the Jianying draft folder; qtrle is lossless and relatively large, suited to seconds-long particles. Note it shares a word with `gtrk render` (full-video rendering) but is a different thing.
415
415
  - **fetch** (`gtrk mg fetch <query|block> [--top 3] [--all]` / `gtrk mg fetch --pick <block> --slot <beat> --project <dir>` / `--pick <block> --as <composition_id> --duration <sec> [--out <dir>]`): the **neutral particle source** from the HyperFrames registry — MG without a show skill. Candidate mode is **offline** (the snapshot ships with the package, pinned to a source commit) and lists 2–3 blocks per query with compat flags and poster links; pick mode fetches the block through three sources in order (our mirror first, then jsdelivr, then GitHub raw; 5 s each, **sha256-verified per source**, nothing is written if all three fail) → eight mechanical rewrites (`<template>` wrap / id rename / drop data-start and fit the slot / sink the solid backdrop into a child / swap GSAP to the contract CDN / swap fonts to a CJK font provable in our runtime mirror / fit duration to the slot / letterbox scaling, never stretch) → `gtrk mg lint` (fatal items block the write) → lands at `<project>/mg/<composition_id>.html`. **A skeleton is not a deliverable**: the returned `editable` (copy / number arrays / colors) MUST be rewritten for the beat and the show before laying. The contract currently accepts 1920×1080 particles only; `excluded` blocks are refused, `review` blocks (canvas / house-rule 8 shapes) can be fetched but need a real render check. `GITRUCK_MG_REGISTRY_BASE` overrides the fetch prefix.
@@ -433,12 +433,12 @@ Project entry points that start from **a voice-over** instead of a talking-head
433
433
  | `gtrk audio lay … --beat-align` | **Climax anchoring** (`audio_music_analyze`, billed once): pins the BGM’s emotional peak `H` (the server’s `highlight.time`) onto the cut’s climax `A`; the mapping is always `track_sec = A + (bgm_sec − H)`. **Both sides long enough ⇒ zero tiling** (exactly one clip); only a short side is tiled outward, with seams snapped to downbeats (the two hard track boundaries are cut without snapping). `A` comes from the criterion chain over `struct_meta.split.beats`: elevation → container flip → callback closure → `0.75×total` fallback, and **the CLI reports which rung was used — the fallback is explicitly flagged as a guess**. Without a key / on analysis failure / when the track has no highlight it degrades to no anchoring and the command never fails over it |
434
434
  | `gtrk audio lay … --beat-align --climax <track-sec>` | Climax **escape hatch**: always overrides the chain above. Out of range (≤ `--offset` or ≥ the project end) or non-numeric **errors out immediately** rather than falling back silently — an explicitly wrong value should be visible at once. Must be used together with `--beat-align` |
435
435
  | `gtrk audio lay … --no-loop` | No tiling: with `--beat-align` it lays only the anchored span and leaves head/tail silence (**the silent seconds are reported**); without `--beat-align` it keeps a single pass instead of looping to fill the project |
436
- | `gtrk audio tighten --project <dir>` | Tightens the pauses **between sentences** in the voice-over track (**purely local, zero billing**): only silence that straddles a sentence boundary is compressed — **within-sentence breaths and quoted original-audio spans are left alone** — and the output honestly reports how many within-sentence breaths were skipped. `--keep <sec>` silence to keep after tightening, `--min-silence <sec>` leave anything shorter untouched, `--boundary-tol <sec>` tolerance for "sits on a sentence boundary", `--dry-run` report how many spots and how many seconds would be compressed without writing. For the three defaults see `gtrk audio tighten --help` (measured, accepted values — tune them for another subject or voice) |
436
+ | `gtrk audio tighten --project <dir>` | Tightens the pauses **between sentences** in the voice-over track (**purely local, zero billing**): only silence that straddles a sentence boundary is compressed — **within-sentence breaths and quoted original-audio spans are left alone** — and the output honestly reports how many within-sentence breaths were skipped. `--keep <sec>` silence to keep after tightening, `--min-silence <sec>` leave anything shorter untouched, `--boundary-tol <sec>` tolerance for "sits on a sentence boundary", `--dry-run` report how many spots and how many seconds would be compressed without writing. For the three defaults see `gtrk audio tighten --help` (tune them for another subject or voice) |
437
437
 
438
438
  `project init` also takes `--canvas <WxH>` (default 1080x1920), `-o/--out`, `--reupload`, `--no-open` and `--json` with the same semantics as `oralcut`; with `--json` both commands emit a single-line result JSON on stdout (human logs go to stderr).
439
439
 
440
- > ⚠️ **`--beat-align` was redesigned wholesale on 2026-09-02** (change `redesign-beat-align-climax-anchor`). The old implementation pushed the whole BGM back by `firstDownbeat` seconds — it analysed the **BGM’s own timeline**, which has no consumer on the project timeline at all, so the net effect was **paying for one cloud analysis in exchange for an equally long silence at the head of the cut**.
441
- > ⇒ **Artifacts produced with `--beat-align` are no longer byte-reproducible**; the default path without that flag is **byte-for-byte unchanged**.
440
+ > **`--beat-align`**: follows the climax-anchor rule in the table above; an explicit `--climax` overrides the automatic selector. The mode calls `audio_music_analyze` and incurs its listed charge.
441
+
442
442
 
443
443
  > **Run `tighten` before laying tracks**: it changes the voice-over track's length, and `gtrk split` / `gtrk matrix` reproject beat timecodes against the project as it stands — tighten first and you save a round of rework. To get it right at synthesis time instead, in-house voices accept `--fragment-interval` during TTS (see `audio_tts_clone` below); cloud voices do not support that parameter, which is when you tighten after synthesis instead.
444
444
 
package/docs/reference.md CHANGED
@@ -234,7 +234,7 @@ gtrk transcript "D:/素材/采访视频.mp4" --lang zh-CN --out "D:/文字稿/
234
234
 
235
235
  | 参数 | 作用 | 缺省 |
236
236
  |---|---|---|
237
- | `-o, --out <dir>` | 产物目录 | **必填**(2026-09-08 起无缺省;`--out .` = 当前目录本身) |
237
+ | `-o, --out <dir>` | 产物目录 | **必填**(`--out .` = 当前目录本身) |
238
238
  | `--render` | 额外本地渲染成片(需原毛片仍在 gtrk 内嵌路径 + ffmpeg) | 关 |
239
239
  | `--jianying-draft-dir <dir>` | 剪映草稿根目录(或 `auto`) | 读 init 配置 / 自动探测 |
240
240
  | `--no-open` / `--json` | 同 `oralcut` | — |
@@ -318,7 +318,7 @@ gtrk patch set --project <dir> --track audio:1 --at 3.0 --volume 0.5
318
318
  | `--highlight-weight <w>` | 仅 `matrix lay`:把「有没有看点」(信息量 / 戏剧性 / 情绪强度 / 稀缺性)融进候选排序,0..1。与 `--mark-weight`(画面好不好看)**正交**,两权之和钳到 1。⚠️ 看点分取 `describe` 的理解缓存,**没跑过 `describe` 就等于没开**——无缓存候选按中性处理,权重回吐给相似度 | `0`(关闭、零回归) |
319
319
  | `--decode-path <mode>` | 仅 `matrix index`:场景检测的解码路 `auto` \| `gpu` \| `cpu` \| `full`。`auto` 自动探测硬解并**逐素材降级**(推荐);`gpu`/`cpu`/`full` 钉死某档且**失败不降级**(对照与排障用)。⚠️ 缺省仍是 `full`(旧行为),要提速得自己传 `auto` | `full` |
320
320
  | `--proxy-width <n>` | 仅 `matrix index`:代理解码宽度。⚠️ 再往下保真度明显劣化,**勿随手调小** | `384` |
321
- | `--proxy-scaler <name>` | 仅 `matrix index`:代理缩放算法。缺省 `neighbor`(点采样不滤波,实测比 `bicubic` 又快又准)。⚠️ 改它基本只有做对照实验才需要 | `neighbor` |
321
+ | `--proxy-scaler <name>` | 仅 `matrix index`:代理缩放算法。缺省 `neighbor`(点采样不滤波);需要其它算法时显式传入 | `neighbor` |
322
322
  | `--exclude-recent <n>` | 仅 `matrix material --scope audio`:选曲避让最近 n 首用过的 BGM。历史由 `audio lay` 落轨**自动记账**,不用自己维护 | `12` |
323
323
  | `--no-exclude-recent` | 关掉上一条的选曲避让,允许复用近期曲目 | 关(缺省避让) |
324
324
  | `--out <file>` | ad-hoc 模式结果落文件;`matrix fetch` 原片落盘目录(绝不写剪映草稿目录) | stdout / `./matrix-fetch/` |
@@ -367,9 +367,9 @@ gtrk patch set --project <dir> --track audio:1 --at 3.0 --volume 0.5
367
367
  >
368
368
  > **机读账面:`counts.results` 是「去重前」口径**:`--json` 的 `counts.results` 恒是**逐 query 累加的检索响应条数**(既有口径不动)——15 条 query 各命中同一条素材时它就是 15,而 plan 落盘可能只有 8 行、只对应 1 个素材。要判「到底有多少料」读派单消费模式另出的三键:`counts.zero_yield`(**真·零产出**的 query 数,判据取**检索响应**为空,而非事后扫 plan 的 `results: []`——beat 内去重会把命中折进同 beat 的兄弟 query,折叠 ≠ 零产出)、`counts.plan_results`(plan **落盘后**的实际 result 行数,去重后)、`counts.distinct_clips`(plan 内 distinct `clip_id` 数)。这三键**只在派单消费模式**出现,ad-hoc `matrix search` 与 `matrix lay` 的 `counts` 逐字节不变(**缺席 = 没这个概念,不是「测出来是 0」**)。铺轨侧同理另出 `lay.beatsWithCandidates`(有候选的 beat 数)与 `lay.emptyBeats`(**零候选 beat 名单**——整段没有任何可铺的画面),两者恒满足 `beatsWithCandidates + emptyBeats.length = plan 的 beat 总数`。
369
369
  >
370
- > **素材落盘自检**:写回工程之后自动查一遍 `materials[].path` 是不是真的都落盘了(**只读、只报不动**)。相对路径恒以 **`.gtrk` 文件所在目录**(`<产物目录>/gtrk/`)为基准解析。`--json` 出 `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }`——`dangling` 是工程自带素材的**悬空引用**(登记在、文件不在)全量清单,每条标出**是否被时间线引用**及引用位置(被引用 = 那一段没素材可放,比孤儿严重得多);绝对路径缺失另计 `external`(外接盘没挂载也会这样,不混进主判);http(s) 素材只计数、**不发网络请求**。**这是告知不是拦阻**:查出悬空不改 `ok`、不改退出码、不删任何素材条目或文件。悬空多半是历史遗留(如客户端「确认原片」下载中断),修法是在客户端重新确认原片或删掉那条 clip。没写回的运行(`--lay 0` / 拒铺 / 工程缺失)**不出 `integrity` 字段**——缺席 = 本次没查,不是「查过且干净」。
370
+ > **素材落盘自检**:写回工程之后只读检查 `materials[].path` 是否落盘。相对路径以 `.gtrk` 文件所在目录(`<产物目录>/gtrk/`)为基准解析。`--json` 出 `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }`;`dangling` 是登记存在但文件缺失的素材,并标出是否被时间线引用,绝对路径缺失另计 `external`,http(s) 素材只计数且不发网络请求。检查只告知,不改 `ok`、退出码、素材条目或文件;修复悬空引用请在客户端重新确认原片或删除对应 clip。未写回的运行(`--lay 0` / 拒铺 / 工程缺失)不出 `integrity` 字段。
371
371
  >
372
- > **纯黑底垫轨**:默认在全部候选轨之下、口播主轨之上垫一条纯黑底轨(`struct_meta.broll.black_track` 记其 `track_index`),按已落成的 beat 包络整条铺满,使 B-roll 期间(含候选轨留空处)不漏出底下的口播画面。**代价是「黑底空洞」**:候选轨没填满的地方就是纯黑压口播,铺轨会把它算出来——`--json` 恒出 `lay.blackBedHoleSec` 与逐段的 `lay.blackBedHoles`,单段 ≥ 3s 或单 beat 占比 ≥ 15% 时另出一条非致命告警(不改退出码、不阻断铺轨),可据此调 `--score-floor`、改用 `--no-black-bed`、或到客户端手动补片。字节落 `assets/builtin/solid-000000-<W>x<H>.png`,与客户端内置纯色素材同 id 命名空间、幂等复用。删候选轨时别误删它;换片请拖到候选轨颗粒上、**别拖到黑底条上**——客户端 0.2.10 起(2026-07-31 发版强更)**拖到黑底条上会被直接拒绝并提示**。若客户端仍是 0.2.10 之前旧版(强更未拉到),旧行为是静默新建一条 video 轨插入、落点在下半区时预览完全看不见(按一次 `Ctrl+Z` 可整条撤销)——先重启客户端吃到强更。不想要黑底加 `--no-black-bed` 重跑即剥净。
372
+ > **纯黑底垫轨**:默认在候选轨之下、口播主轨之上铺满已落成的 beat 包络,避免 B-roll 留空处露出底下画面。候选轨未填满的部分会形成「黑底空洞」;`--json` 恒出 `lay.blackBedHoleSec` 与 `lay.blackBedHoles`,单段 ≥ 3s 或单 beat 占比 ≥ 15% 时给非致命告警。可调 `--score-floor`、改用 `--no-black-bed` 或在客户端补片。素材落在 `assets/builtin/solid-000000-<W>x<H>.png` 并幂等复用;换片请拖到候选轨,不要拖到黑底条。客户端 0.2.10+ 拖到黑底条会直接拒绝,旧客户端先升级;不需要黑底时用 `--no-black-bed` 重跑。
373
373
 
374
374
  **本地素材模式(`matrix index` / `--local`)**:素材不必入云端素材库,用你本地的素材文件夹(视频+图片混合)直接检索铺轨:
375
375
 
@@ -394,11 +394,11 @@ gtrk matrix lay --project <目录> [--plan <path>] # ③ 消费(
394
394
  >
395
395
  > **`false` 不要读成「无版权、可以随便用」——它恰恰相反。** 决定性反证:他人版权的概念素材入库固定写 `is_copyright=0`;这个字段若真是「是否受版权保护」,那批必须是 1。
396
396
  >
397
- > 这条警示是真机踩出来的:2026-09-02 有 AI 执行方连着两轮**特意去挑 `false`** 当「安全选择」,把不可商用素材铺进了 5 个工程,而唯一安全的 `true` 反倒被主动避开。所以 `--json` 里 CLI 会逐条**派生一个人话标签 `copyright_label`**(`"可商用"` / `"不可商用"`)——它由 `is_copyright` 推出、与之恒同向、缺席同缺席,判读认这两个键之一即可,别靠字段名去猜。
397
+ > `is_copyright` 读作「能不能商用」:`true` = 可商用,`false` = 不可商用。`--json` 逐条派生 `copyright_label`(`"可商用"` / `"不可商用"`),它与 `is_copyright` 同向,字段缺席时也缺席;判读使用这两个键,不要从字段名猜语义。
398
398
  >
399
399
  > 该字段**只有矩阵成员口才有**:公开口没有它不是「不可商用」,而是服务端在源头就只放可商用素材(缺席即无需判,CLI 如实缺省、绝不补假值)。
400
400
  >
401
- > **缺省口径(⟲ 2026-09-06)**:矩阵成员档**缺省搜全库**(`copyright_scope=all`,含非商用/概念素材)——那正是成员身份买到的东西,命令不会替你收紧,随包 skill 也不许「保险起见」自行剔掉 `is_copyright:false` 的候选(但会**逐条如实标注**版权状态)。只要可商用时,自己加 `--commercial-only`。
401
+ > **缺省口径**:矩阵成员档默认搜全库(`copyright_scope=all`,含非商用/概念素材),不会替你收紧;随包 skill 也不应自行剔除 `is_copyright:false`,而应逐条标注版权状态。只要可商用时,显式加 `--commercial-only`。
402
402
 
403
403
  编排配方(纯匹配 / 先理解后铺 / 时间窗 / 素材先行编剧 / 三层层叠)与 plan 编辑口径见随包 skill `/gtrk-matrix`。
404
404
 
@@ -421,14 +421,14 @@ gtrk matrix lay --project <目录> [--plan <path>] # ③ 消费(
421
421
 
422
422
  - **铺轨**(`gtrk mg --project <dir>`):读 `dispatch.mg` → 逐 beat 从 `<project>/mg/<composition_id>.html` 取源颗粒 → lint → 铺进 `beat_track`,把 `struct_meta.mg` 原子写回 `.gtrk`(幂等登记自产轨 `lay_tracks`,重铺先剥旧自产物再 append、用户手加轨零连带)。「透明叠加 / 满屏底层」由颗粒 HTML 根 `background` 反推的 `opaque` 决定。缺 HTML / lint 失败的 beat 计入 `skipped`、不拦其余。
423
423
  - **剥离面 ≠ 「本次铺什么」,也 ≠ 「登记轨全集」**:`--only <beat>` **只剥命中的那几颗**(真增量合并)——轨上其余已铺颗粒的 clip / 素材 / 登记条目**原样保留**,连同用户在 opencut 对它们的手调(保留的是既有 clip 原件,非照登记重建,故透明度 `opaque` 不会丢);这些保留条目**不重新 lint、不重新复制源 HTML**(工程自包含,`<project>/mg/` 下源文件删了也不影响)。全量重铺仍是「剥净再整轨重建」,**唯一例外**是本次派单里有、却因缺 HTML / lint 未过 / 重投影后零存活而**没铺成**的那几颗——它们上一轮的 clip 保留在轨上(不因为新的做坏了就把旧的也毁掉);反之**派单里已不存在**的已铺条目仍照剥(计划变更 ≠ 做坏了)。要连其余已铺颗粒一起剥掉重来:`--replace-all` 显式授权。
424
- - **素材表不囤积**:素材的剥离键按「**自产身份 × 零引用**」判(自产 = `mg-`/`rrv-` 前缀 **或** 落在 CLI 独占的 `assets/mg/` 下且文件名在自产登记里),**不认客户端可改写的 `html_material` 前缀**——所以在 opencut 里编辑过工程之后重铺,旧素材照样剥得掉,`mg-` 素材数**恒等于轨上颗粒数**,历史遗留的重复 / 孤儿条目一并清掉。**非自产素材零连带**(`broll-*` / `ex-solid-*` / 你自加的,哪怕零引用也不碰);仍被存活 clip 引用的自产素材也不剥(不会剥出失联 clip);盘上 `assets/mg/` 的 html 副本从不删。
424
+ - **素材表不囤积**:素材按「自产身份 × 零引用」剥离(自产 = `mg-`/`rrv-` 前缀,或位于 CLI 独占的 `assets/mg/` 且文件名在自产登记里),不依赖客户端可改写的 `html_material` 前缀。`mg-` 素材数与轨上颗粒数保持一致;重复或孤儿条目会清掉。非自产素材、仍被存活 clip 引用的自产素材,以及盘上 `assets/mg/` 的 HTML 副本都不动。
425
425
  - **「一条都没定位到」不是清空指令**:`--only` 打空、`dispatch.mg` 为空/缺失、或本次条目全被 skip,**而轨上已有已铺颗粒**时,同样拒绝写回(那是派单或选择器出问题的信号)。确要清空加 `--replace-all`。首次铺轨(轨上本就没有已铺条目)不受此限,照常走完报 `laid=0`。
426
426
  - **槽位窗口现场重投影**:铺轨与 lint 之前先用「`transcript` × 当刻 `.gtrk`」重算每条队列条目的 `[track_st, track_ed]`,之后 lint 的坑位包络(铁律⑦)与落轨 clip 时长一律以重算值为准(`--only` 同守;aux 派生颗粒按**自己的** span 重投影,不与主 beat 窗口混同)。`dispatch.mg` 里的时码只是**投影时刻快照**,仅在重投影不可行时兜底——**改完口播轨直接铺即可,不必先重跑 `gtrk split`**。`--json` 恒出 `reprojection:{mode,degraded,reason?,drifted,max_offset,shrunk,dropped}`(`--lint-only` 也有)。重投影后**零存活**的条目 skip 并计入 `skipped`(不复制 HTML、不按快照铺回去);重投影不可行(transcript 缺失 / 工程定位不到 / 主轨查不到口播素材)→ 降级用快照 + 告警 + `--json` 标注,退出码不变;工程**非 v1** 的既有行为不变(铺轨路径版本门非 0 退出、`--lint-only` 照旧出报告)。铺轨成功会把本次时码来源(`timecode_source` / `reprojected_at`)纯追加登记进 `struct_meta.mg`。
427
427
  - **lint**(`gtrk mg lint <颗粒.html> [--dispatch <path>]`):纯本地静态校验颗粒 HTML 的铁律机器可判定子集(`<template>` 包裹、`data-composition-id` + 1920×1080、`gsap.timeline({ paused: true })`、`window.__timelines` 注册、无 `Math.random` / `Date.now`、自包含无相对外链、根 `background` 与 `opaque` 自洽…);给 `--dispatch` 时校验 `composition_id` 命中派单。任一致命项非 0 退出并逐条报因。
428
428
  - **期望 id 一致性**(`1-cid-expect`,**致命**):HTML 内 `data-composition-id` 必须等于期望 id(铺轨=该条派单的 `composition_id`;`mg lint`=文件名,仅当它命中派单或形如 `…-B<数字>[-aux<n>]` 时比对,`./tmp.html` 这类改过名的副本不比对)。防的是「复制 `<id>.html` 改名时漏改内部 id」——落轨会写出以文件名命名的 clip/material,而文件注册的是另一个 `__timelines` 键、还与同名颗粒抢同一个样式作用域。
429
429
  - **铁律⑦ tl 总长估长**(`7-fill-slot` / `7-no-estimate` / `7-infinite-repeat`,**恒非致命、不拦铺轨**):已知坑位包络时(铺轨逐颗;`mg lint --dispatch` 命中派单条目)对 GSAP 时间线做**静态下界估算**——逐调用降级,能解析的计入(`duration×(repeat+1) + repeatDelay×repeat`,`yoyo` 不加时长),表达式 position / 非字面量 duration 那条**跳过不计**(忽略若干调用仍是合法下界)。估长 < 包络 → 告警;一条都算不出 → 显式提示「无法静态估长,铁律⑦未校验,须真引擎 seek 验收」(**不静默**,「算不出」与「算过且通过」在输出上可区分);含 `repeat:-1` → 告警「无限循环令总长 Infinity、铁律⑦不可静态验证,请改按坑位算死的有限 repeat」。真判据永远是渲染引擎逐帧,本项只做提醒层。
430
430
  - **铁律⑧重复图元合并**(`8-primitive-merge`,**恒非致命、不拦铺轨**):识别「循环体内创建,或由循环调用具名工厂创建;落到同一父节点;且没有逐元素动画驱动」的可合并 `line` / `rect` / `path` / `polyline` / `polygon` 批次。同一父节点的纯数字循环 trip count 累加后 **≥ 8** 才报数;边界含 `.length` / 具名常量而算不出时仍报「条数未知」,不做常量折叠;逐元素 `gsap.set` / tween 或被 tween 首实参使用的元素数组会被排除。本项只提示「这里有一批可**无损**合并的重复图元,合并后画面逐像素不变」,**不是风险判定**:命中不代表该颗粒会复现缺陷,未命中也不代表安全,真判据仍是真渲染出片抽帧。
431
- - **回调与 seek 语义**(`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`,**恒非致命、不拦铺轨**):对齐契约同名一节(2026-07-26 增补)。GSAP `seek(t)` 默认抑制回调 → 补间属性照常插值、但 `onUpdate` 里的 DOM 写入不执行,翻车形态是**画面定在初始态而非黑屏**。契约把保证压在**引擎侧**(定帧 MUST 用 `seek(t,false)` / `time(t)` / `progress(p)`),故颗粒**用回调驱动画面是合规写法**;lint 这三项只是**哨兵**:`x-callback-driven` = 回调写 DOM 且无任何 seek 兜底(有兜底则沉默,避免重复提醒);`x-engine-api-override` = 颗粒运行时覆写 `tl.seek` 或把 `__timelines[…]` 换成包装对象(会推翻引擎显式传的 `seek(t,true)`,且引擎改走 `time()`/`progress()` 即失效,属过渡态);`x-raf-interval` = 含 `requestAnimationFrame(` / `setInterval(`(自有时钟不被 seek,等于冻结)。三项 MUST NOT 致命——「用回调驱动画面」不是违规。
431
+ - **回调与 seek 语义**(`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`,**恒非致命、不拦铺轨**):对齐契约同名一节。引擎定帧 MUST 使用 `seek(t,false)` / `time(t)` / `progress(p)`;颗粒可用时间线回调驱动画面,但不得覆写 `tl.seek` 或替换 `window.__timelines[…]`。三项 lint 只作提醒,不要求作者添加垫片。
432
432
  - **status**(`gtrk mg status --project <dir>`):汇总 MG 流水线——`dispatch.mg` beat 总数 / 已产源 HTML 数 / 已铺进 `.gtrk` 数,并逐 beat 标注(缺 HTML / 已产未铺 / 已铺)。
433
433
  - **render**(`gtrk mg render <颗粒.html> --duration <sec> [--out <dir>] [--yes]`):**脱离工程**把单颗颗粒云渲成剪映可读的 qtrle 透明 alpha MOV(精剪补给口——粗剪导剪映后缺一颗动态图,不回客户端就能补)。链路 = lint 前置(包络 = `--duration`,任一致命项本地拦截、零提交零计费)→ **计费预估确认**(实时查价;CLI 无本地 HTML 渲染引擎,独立颗粒唯一路 = 云渲计费任务,`--yes` 跳过确认)→ 内联提交云端 → `<composition_id>.mov` 落盘 + `task.json`/`result.json` 面包屑(崩溃可凭 task_id 恢复)。**射程**:首发只出 qtrle(`--format webm` 明确拒绝,剪映不吃 VP8-alpha)、只收 1920×1080 颗粒(契约未开竖屏/异形口)、`--duration` 必填。产物不进 `.gtrk`、不写剪映草稿目录;qtrle 无损体积偏大,适合秒级颗粒。注意与 `gtrk render`(整片成片渲染)同词不同物。
434
434
  - **fetch**(`gtrk mg fetch <检索词|块名> [--top 3] [--all]` / `gtrk mg fetch --pick <块名> --slot <beat> --project <dir>` / `--pick <块名> --as <composition_id> --duration <sec> [--out <dir>]`):**registry 中性颗粒源**——没建栏目也能出 MG。候选态**离线**(快照随包、钉死来源 commit)按检索词列 2–3 件(含 compat 标记与海报链接);取块态按三源顺序取块(我方镜像优先、jsdelivr、GitHub raw,每源 5 s,**逐源 sha256 强校验**,三源皆败零落盘)→ 机械改写八条(`<template>` 包裹 / 改 id / 去 data-start 贴坑位 / 实心底下沉子层 / GSAP 源换契约 CDN / 字体换运行时镜像可证的 CJK 字体 / 时长贴坑位 / 信箱缩放不拉伸)→ `gtrk mg lint` 致命项不过不落盘 → 落 `<project>/mg/<composition_id>.html`。**骨架不是成品**:返回的 `editable`(文案 / 数值数组 / 色值)MUST 按 beat 与栏目改写后再铺。契约当前只收 1920×1080 颗粒,其它画布拒绝;`excluded` 件拒取,`review` 件(canvas / 铁律 8 高危形态)可取但须真渲验收。`GITRUCK_MG_REGISTRY_BASE` 可整体覆盖取块前缀。
@@ -456,12 +456,12 @@ gtrk matrix lay --project <目录> [--plan <path>] # ③ 消费(
456
456
  | `gtrk audio lay … --beat-align` | **高潮点锚定**(`audio_music_analyze`,计费一次):把 BGM 的情绪峰值 `H`(服务端 `highlight.time`)压到成片高潮点 `A` 上,映射恒为 `轨秒 = A + (BGM 秒 − H)`;**两侧都够长就零平铺**(恰好 1 个 clip),不够长才按小节线平铺补齐、接缝吸附 downbeat(轨的两个硬边界处的截断不吸附)。`A` 取自 `struct_meta.split.beats` 的判据链:升华段 → 容器转折 → 回扣段 → `0.75×全片`兜底,**命中哪一档 CLI 如实报出,兜底档会明示是猜的**。无 Key / 分析失败 / 曲子缺高潮点一律降级为不锚定,命令不失败 |
457
457
  | `gtrk audio lay … --beat-align --climax <轨秒>` | 高潮点**逃生门**:一律覆盖上面那条判据链。越界(≤ `--offset` 或 ≥ 工程末尾)或非数**直接报错**,不静默回落 —— 显式给错了值要当场知道。须与 `--beat-align` 同用 |
458
458
  | `gtrk audio lay … --no-loop` | 不平铺补齐:开了 `--beat-align` 时只放锚点那一段、头尾留白(**留白秒数如实报出**);未开 `--beat-align` 时保留单次、不循环叠满至工程末尾 |
459
- | `gtrk audio tighten --project <目录>` | 收紧配音轨的**句间**停顿(**纯本地、零计费**):只压跨句界的静音,**句内换气与原声引用段不动**,出参如实报「跳过句内换气 N 处」。`--keep <秒>` 收紧后保留的静音、`--min-silence <秒>` 短于此不动、`--boundary-tol <秒>` 判「贴着句界」的容差、`--dry-run` 只报会压几处共几秒、不写盘。三个缺省值见 `gtrk audio tighten --help`(实测认可值,换题材/音色可调) |
459
+ (缺省值见 `gtrk audio tighten --help`,可按题材/音色调整)
460
460
 
461
461
  `project init` 另有 `--canvas <WxH>`(默认 1080x1920)、`-o/--out`、`--reupload`、`--no-open`、`--json`,语义与 `oralcut` 一致;两命令 `--json` 恒出单行结果 JSON(人读日志走 stderr)。
462
462
 
463
- > ⚠️ **`--beat-align` 的语义在 2026-09-02 整套换过**(change `redesign-beat-align-climax-anchor`,主理人拍板)。旧实现是「把整条 BGM 后推 `firstDownbeat` 秒」—— 分析的是 **BGM 自己的时间轴**,工程时间轴里根本没有它的消费方,净效果只是**付一次云端分析的钱换来片头一段等长静音**。
464
- > ⇒ **开过 `--beat-align` 的旧产物不再逐字节可复现**;没开这个 flag 的缺省路径产物**字节零变化**。
463
+ > **`--beat-align`**:按表格中的高潮点锚定规则工作;显式 `--climax` 会覆盖自动判据。该模式会调用 `audio_music_analyze` 并产生相应计费。
464
+
465
465
 
466
466
  > **`tighten` 该在铺轨之前跑**:它会改配音轨时长,`gtrk split` / `gtrk matrix` 的 beat 时码按当刻工程重投影——先收紧再铺轨,省一次返工。想要合成时就对,自训音色可在 TTS 阶段直接传 `--fragment-interval`(见下节 `audio_tts_clone`);云引擎音色不支持该参数,才用本命令在合成之后收。
467
467
 
@@ -520,7 +520,7 @@ gtrk matrix lay --project <目录> [--plan <path>] # ③ 消费(
520
520
  - `gtrk tool image_move ./photo.jpg [--motion zoom_in_center] [--json]` — 图转运镜;产物落 `photo-image_move/`。`--motion` 显式指定运镜方式(26 值:平移 8 `up_to_down`/`down_to_up`/`left_to_right`/`right_to_left` 及四对角线、放大锚点 9 `zoom_in_{up,down,left,right,left_up,right_up,left_down,right_down,center}`、缩小锚点 9 `zoom_out_` 同九方位),未传由云端自动选择;`--param width=1080 --param height=1920` 覆盖推导几何。
521
521
  - `gtrk tool image_matting ./portrait.jpg` / `gtrk tool video_matting ./clip.mp4` — 图片/视频抠像。
522
522
  - `gtrk tool image_blackborder_remove ./photo.jpg [--json]` — 自动裁去单张图片四周黑边。
523
- - `gtrk tool image_canvas_adapt ./photo.jpg --canvas-width 1080 --canvas-height 1920 --canvas-type rectangle [--json]` — 图片比例转换;省略画布参数时沿用服务端默认。画布模式按实际运行时契约只接受 `normal`、`rectangle`、`square`,不接受旧文档中的 `fit`。
523
+ 仅接受 `normal`、`rectangle`、`square`
524
524
  - `gtrk tool image_purify ./photo.jpg [--json]` — 清理你有权处理的图片中的水印、Logo 或叠加元素。
525
525
  - `gtrk tool image_purify ./photo.jpg --purify-scope region --purify-region 0.02,0.02,0.15,0.08 [--json]` — 按框直接去除:不做识别,**框内全部内容都会被处理**;框越小、越贴近要去除的元素效果越好。
526
526
  - `gtrk tool video_blackborder_remove ./clip.mp4 [--json]` — 自动裁去单条视频四周黑边并保留原音轨。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitruck/cli",
3
- "version": "1.2.15",
3
+ "version": "1.3.0",
4
4
  "description": "同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通。",
5
5
  "license": "MIT",
6
6
  "author": "Gitruck (同合云)",
@@ -24,7 +24,7 @@ return-v1 标准包由 `gtrk ai-drama lay` 确定性回填;其他平台没有
24
24
 
25
25
  **你与 `matrix` 同属第 ③ 步,MUST 在 MG 之前落位。** 理由:**AI 情景片段属于底轨 B-roll 画面家族,不是叠加层**——整条管线里唯一的叠加层是 MG(含 ov)。MG 的排版决策是「因势象形避主体」、**依赖底轨的最终画面构图**;若你的片段在 MG 之后才回铺,底轨构图就变了,先产的 MG 是对着「还没有 AI 画面的底轨」做的避让,你的片段落位后必然被避让错位、甚至被 MG 盖住画面主体。
26
26
 
27
- > ⚠️ **本节旧口径是错的,已于 2026-08-17 纠正**:旧文写「第 ⑤ 步、最后一层视觉,越往后叠得越上层」——**那是把「工序次序」误当成了「图层次序」**。工序上你必须先落位;图层上你本来就在底轨、从来不是「最上层」。若你在别处读到「AI 情景动画最后上」的表述,以本节为准。
27
+ > ⚠️ **工序顺序**:AI 情景片段必须先落位;它属于底轨 B-roll,不是最后叠加的图层。之后再做全局构图检查与 MG。
28
28
 
29
29
  **异步等待的口子(你造成的那个)**:你产完描述后,用户要去外部平台出片、抽卡可能数天。严格串行会把 ④⑤ 无限期卡住,故约定:进 ⑤ 的硬门是「**AI 片段已回铺 ∨ 用户明示先跳过**」。走「明示跳过」时,下游 `/gtrk-mg` MUST 把与你的 beat 相邻/重叠区间的 MG 颗粒标记为「AI 回铺后待复查构图」并在收口时复述——**你在交代进度时也要把这个代价说清**,让用户知道跳过意味着将来可能返工那几颗。
30
30
 
@@ -37,11 +37,11 @@ return-v1 标准包由 `gtrk ai-drama lay` 确定性回填;其他平台没有
37
37
  3. **中英双版地位平等,不是主辅。** ①视觉基调、②故事背景、③角色、④分镜四段每一段都给完整的中文版 + 英文版(不是把中文翻一句了事)。中文喂可灵 / 即梦 / Vidu / 通义,英文喂 Veo / Runway / LTX / Luma,用户按目标平台选。**唯独 ⑤ 原文文稿是中文口播原文、不翻译**(它是源真相 + 手动拼接的对位锚,翻译反而失真)。
38
38
  4. **内容可移植,Markdown 外壳不可自由改写。** 默认产物 MUST 通过 `gitruck-ai-drama-desk` 的 Markdown 解析契约:标题必须含 `(beat BXX)`;两大块必须叫 `## 一、中文稿…` / `## 二、English Storyboard…`;角色必须用 `#### 角色名(备注)` 标题;中文镜头必须用 `#### 分镜 01 · …`,英文镜头必须用 `#### Shot 01 · …`;元信息必须含 `区间总时长:track_st … → track_ed … ≈ … 秒`。`镜头`、粗体行、项目符号角色、`## 中文` / `## English`、`工程时间位` 都不是等价写法。**人读友好不能牺牲机读契约。**
39
39
 
40
- > **产物落点纪律(MUST · 全文见随包 `AGENT.md` 同名一节)**:
40
+ > **产物落点纪律(MUST · 全文见随包 `AGENT.md` 同名一节)**:
41
+ > 交付物 MUST NOT 先写 agent 自有目录再拷进工程;中转本身即违规,体积不是豁免理由。
41
42
  > 分镜稿只落**工程目录**(`<project>/ai-drama/<beat_id>.md`)或**用户显式指定处**;
42
43
  > **MUST NOT** 把用户媒体(毛片 / 成片 / 预览 / 外部平台出的片段)或任何大媒体文件复制到 agent 自有工作目录(如用户文档目录下 agent 产品自建的目录、agent 家目录缓存、会话工作区)——需要引用媒体时**用原路径引用**,不做副本;
43
- > 临时文件一律放系统 temp 且**用完即删**(含中断 / 失败路径)。违者后果 = 用户系统盘被静默吃满(真机事故,非假设)。
44
- > 交付物(颗粒 HTML / 工程文件 / 派单稿 / 文稿等)SHALL **直接以工程目录内的最终路径为写入路径**,MUST NOT 先写 agent 自有工作目录再拷进工程——**中转本身即违规**,不以「最后拷进去了」免责,**体积不是豁免理由**(2026-09-07 事故里漂掉的是 2.7–6.2 KB 的 HTML)。与上一条的分界:抽帧图这类**不交付**的中间物走系统 temp;**要交付的东西没有暂存态**。
44
+ > 临时文件一律放系统 temp 且**用完即删**。交付物直接写入工程目录或用户显式指定的路径;引用媒体用原路径。
45
45
 
46
46
  ## 业务分离:栏目 Style Lock 从哪来(关键)
47
47
 
@@ -114,7 +114,7 @@ return-v1 标准包由 `gtrk ai-drama lay` 确定性回填;其他平台没有
114
114
 
115
115
  **Step 4 · 写四段描述(中英分块)**:先出**中文稿**(② 故事背景末句锁调 → ③ 角色结构化 + 禁变项 + 质感落点,复现理论家 pin 复用同段文字 → ④ 分镜每镜前缀复述基调 + 建议秒数 + 对应原文句 + 蒙太奇级意图 → ⑤ 原文文稿中文逐句不翻译),再整块出 **English Storyboard**(① Style Lock + ②③④ 英文原生化,不含 ⑤)。中英**分两大块、不逐段交错**(craft C)。
116
116
 
117
- **Step 5 · 校验后落盘**:先把每个 beat 的候选稿写到系统 temp,运行随 skill 分发的 `scripts/validate-storyboard.mjs <候选稿.md>`;退出码为 0 才写到 `<project>/ai-drama/<beat_id>.md`。失败就按输出逐项修正,MUST NOT 把未通过稿件落进工程。校验器不可用时,至少逐项执行下面「解析器硬契约」与交付前自检。用户直投单段时同样先校验,再落到用户指定处或 `<cwd>/ai-drama/<自拟标题>.md`。临时稿无论成功失败都删掉。
117
+ **Step 5 · 校验后落盘**:先把每个 beat 的候选稿写到系统 temp,运行随 skill 分发的 `scripts/validate-storyboard.mjs <候选稿.md>`;退出码为 0 才写到 `<project>/ai-drama/<beat_id>.md`。失败就按输出逐项修正,MUST NOT 把未通过稿件落进工程。校验器不可用时,至少逐项执行下面「解析器硬契约」与交付前自检。用户直投单段时同样先校验,再落到用户指定的工程路径。临时稿无论成功失败都删掉。
118
118
 
119
119
  ## 输出契约(落盘 Markdown 结构 · 中英分块)
120
120
 
@@ -250,7 +250,7 @@ face / hair / eyes / wardrobe / signature prop + do-not-change + one line on how
250
250
 
251
251
  命令自己解析 id → 导出包目录,走两条**确定性**路径:① 工作台服务可达就问它;② 服务没起就读 `~/.gitruck/ai-drama-desk.json` 的落脚点。两条都不通会报错并给出路。
252
252
 
253
- > ⚠️ **拿到 id 之后 MUST NOT 去磁盘上找导出包**(`find` / `ls -R` / 通配一律禁止)。这是 desk 仓 `agent-project-handoff`「仓库定位只在必要时发生」在 CLI 侧的镜像纪律——2026-09-18 真机就因为缺这条路而跑了一次跨盘全量扫描。解析不出来就如实报、请用户启动工作台或直接给路径,**别自己找**。
253
+ > ⚠️ **拿到 id 之后 MUST NOT 去磁盘上找导出包**(`find` / `ls -R` / 通配一律禁止)。解析不出来就如实报,请用户启动工作台或直接给路径。
254
254
  > 零命中时命令会列出「已查过哪些数据根」:源码部署与打包部署的数据根不同,项目找不到多半是**它在另一个部署里**,不是丢了。
255
255
 
256
256
  已经手上就有目录路径(或用的是非标准平台)时,仍可用 `--package "<exports/aidrama>"`,两者可混用、按路径去重。
@@ -258,7 +258,7 @@ face / hair / eyes / wardrobe / signature prop + do-not-change + one line on how
258
258
  命令会先按当刻口播工程重投影 beat 窗口,再把所有镜头复制到 `<gtrk目录>/assets/ai-drama/<slug>/`,共用一条新的 `video_track`;每个 beat 从起点顺排,最后一镜吸收到 beat 终点。既有 A-roll、BGM 与 B-roll 轨零改动。若已铺 AI 轨被用户手调过,缺省拒绝覆盖;只有用户明确同意丢弃手调时才加 `--replace-all`。
259
259
  3. **非标准平台才手动回铺**:外部平台没有 return-v1 清单时,才由用户把片段拖回 opencut 的 **AI_DRAMA 车道**,对齐该 beat 的 `track_st→track_ed`。AI 出片仍是外部异步、用户驱动;未拿到完整片段前要停下等用户,别替他假装出片 / 回轨完成。
260
260
  4. **回铺齐 ≠ 收口,还有两步**:你的片段回铺齐只意味着 **③ B-roll 底轨阶段**完成。接着 MUST 走 **④ 全局抽帧检查画面构图**(对三源合并后的最终底轨抽帧,看主体位置 / 安全区 / 画面朝向 / 明暗,**停下等用户确认**)→ 过了才交棒 **⑤ `/gtrk-mg`** 把 MG(含 ov)叠上 → 最后才 `gtrk render` 收口。
261
- ⚠️ **MUST NOT 从你这里直接跳到 `render`**(旧文档曾这么写,那是错的——它把 AI 情景动画当成了最后一层)。
261
+ ⚠️ **MUST NOT 从你这里直接跳到 `render`**;先完成全局构图检查与 MG,再进入字幕和渲染。
262
262
 
263
263
  > 原则:**agent 替用户跑 CLI / 接力 skill,用户只对话**。但 AI_DRAMA 车道天然有「用户去外部平台出片 + 手动拼回」的手工环节——那部分是外部的手,无法替跑;除此之外的解析、生成、落盘、交代,全由你(脑)一次做完,别让用户自己去拼描述。
264
264
 
@@ -19,11 +19,11 @@ description: 封面工作台——把一期视频的封面从「设计诊断 +
19
19
  2. **两阶段不能合并。** 用户还没抽图就端出工作台 = 盲排(不看背景图排字,等于退回指令稿时代);工作台的价值就在「因势象形地就着背景图排字」。阶段一交付后**停下等图**。
20
20
  3. **审美决策由本 skill 做,工作台只暴露微调面。** 工作台暴露给用户的只有:图层位置 / 字号 / 文案 / 显隐、显式声明的装饰件开关、T1 全局样式滑杆(描边 / 发光 / 既定字色选项)、**T1 字体与字重档位(字体列表读用户本机已装字体,默认字仍由本 skill 定)**。色板体系、字体选型、布局拓扑、装饰件组合是审美决策——由你按注入的风格资产做出、交付时说明理由;用户想改审美层,**经对话增量改 HTML**(不推倒重来),别让他在工作台里找不存在的控件。
21
21
 
22
- > **产物落点纪律(MUST · 全文见随包 `AGENT.md` 同名一节)**:
22
+ > **产物落点纪律(MUST · 全文见随包 `AGENT.md` 同名一节)**:
23
+ > 交付物 MUST NOT 先写 agent 自有目录再拷进工程;中转本身即违规,体积不是豁免理由。
23
24
  > Prompt 文本与工作台 HTML 只落**用户显式指定目录**,或有工程上下文时落 `<project>/cover/`;
24
25
  > **MUST NOT** 把用户媒体(背景图 / 导出的封面 PNG / 任何大文件)复制到 agent 自有工作目录(如用户文档目录下 agent 产品自建的目录、agent 家目录缓存、会话工作区)——压缩背景图时**从原路径读取**,压缩产物直接以 base64 写进工作台 HTML,不留中间副本;
25
- > 压缩脚本等临时文件一律放系统 temp 且**用完即删**(含中断 / 失败路径)。违者后果 = 用户系统盘被静默吃满(真机事故,非假设)。
26
- > 交付物(颗粒 HTML / 工程文件 / 派单稿 / 文稿等)SHALL **直接以工程目录内的最终路径为写入路径**,MUST NOT 先写 agent 自有工作目录再拷进工程——**中转本身即违规**,不以「最后拷进去了」免责,**体积不是豁免理由**(2026-09-07 事故里漂掉的是 2.7–6.2 KB 的 HTML)。与上一条的分界:抽帧图这类**不交付**的中间物走系统 temp;**要交付的东西没有暂存态**。
26
+ > 压缩脚本等临时文件一律放系统 temp 且**用完即删**。交付物直接写入工程目录或用户显式指定的路径;引用媒体用原路径。
27
27
 
28
28
  ## 业务分离:栏目封面风格从哪来(关键)
29
29
 
@@ -17,9 +17,9 @@ description: 美食解说垂类图纸(解说链示例)——把探店/密着
17
17
  | 形态 | 输入 | 时序档位(正本 §三) |
18
18
  |---|---|---|
19
19
  | **探店 · 密着** | 一条纪实长片(二创;带字幕文件的直接当台词锚,无则转写)或自拍探店素材夹 | 软窗档缺省:进店→点单→出品→吃→人的故事,流程线单调、允许蒙太奇跳 |
20
- | **做饭流程** | 一条做饭记录或素材夹 | 软窗档且**步骤线忠实**:工序 MUST NOT 乱序重排(先炸后腌是事故) |
20
+ | **做饭流程** | 一条做饭记录或素材夹 | 软窗档且**步骤线忠实**:工序 MUST NOT 乱序重排 |
21
21
 
22
- 稿源与声音基座随正本开工问询(AI 代写/自备稿;TTS/自己念)。⟲ **推荐面(公约 §三‴)**:问答收口时多问一句「要不要看看第三方 skill?」,不列名;题材命中场景触发词才多 1–2 句定向推(`gtrk skills recommend --scene <id> --json` 取 tier 最高者,各给一句为什么),用户追问才展开完整块;轻提加定向合计不超三句、同会话同场景只推一次、答「不要」零动作。**MUST NOT 执行任何网络下载**;二创长片是第三方作品——版权与平台二创规则责任归用户,开工先说。
22
+ 稿源与声音基座随正本开工问询(AI 代写/自备稿;TTS/自己念)。**推荐面(公约 §三‴)**:问答收口时可问一句「要不要看看第三方 skill?」;题材命中场景触发词才定向推荐,读取 `gtrk skills recommend --scene <id> --json`;用户拒绝后不执行推荐动作。**MUST NOT 执行任何网络下载**;二创长片是第三方作品,版权与平台二创规则责任归用户。
23
23
 
24
24
  ## 二、美食要素层(写稿规约,叠在通用框架层之上)
25
25
 
@@ -32,12 +32,12 @@ description: 美食解说垂类图纸(解说链示例)——把探店/密着
32
32
  禁拟声堆砌与「入口即化」式空转(文风铁律照打);
33
33
  - **数字如实**:分量/价格/营业时间只用素材里可见可听的,MUST NOT 编造;
34
34
  **时效丑话**:价格与营业信息随拍摄时点变动,成片引用时提醒用户自行核对;
35
- - **背景检索建议**(旅拍 §三 通用框架层同款,2026-08-28 拍板):对店名/地段/菜系关键词
35
+ - **背景检索建议**(旅拍 §三 通用框架层同款):对店名/地段/菜系关键词
36
36
  做一轮背景检索补写稿语料(店家来历、当地饮食脉络),有检索能力就做、没有不强求;
37
37
  检索所得与素材互证后才入稿、档案标注来源;
38
38
  - **做饭流程线**:步骤忠实、关键动作给特写位(刀工/火候/翻面)、成品定格收束。
39
39
 
40
- ## 二′、L1 看点 rubric · 画面投影版(260828 新增,供 `--highlight-weight` 用)
40
+ ## 二′、L1 看点 rubric · 画面投影版(供 `--highlight-weight` 使用)
41
41
 
42
42
  `highlight` 是**镜头看点分**——服务端 VLM 对着一帧画面打 0–100。喂给它的评分准则
43
43
  (`highlight_rubric`)按垂类走,美食这一档取下面这份:
@@ -58,8 +58,7 @@ description: 美食解说垂类图纸(解说链示例)——把探店/密着
58
58
  >
59
59
  > 两侧的分工是:**画面 rubric 管「这一帧值不值得看」,写稿管「这件事值不值得讲」。**
60
60
 
61
- **怎么把它真的传进去**(2026-09-09 补:此前只有准则文本、没有入口,照做也传不进去,
62
- `--highlight-weight` 打的一直是服务端领域无关缺省分):把上面代码块**原样**存成工程目录下的
61
+ **传入方式**:把上面代码块**原样**存成工程目录下的
63
62
  `highlight-rubric.txt`,理解那一步带上它——
64
63
 
65
64
  ```bash
@@ -94,18 +93,17 @@ BGM 检索词照公约 §三′ 内容驱动(这家店的气质:地域/年
94
93
  带上 `--highlight-rubric @<工程>/highlight-rubric.txt`**——不带就是按服务端缺省准则打分,
95
94
  美食这一档的分量对比/价格实物/名场面动作全不算数;两样都漏则全部候选中性、权重白传,lay 会就此告警)。
96
95
  BGM 选带人声歌曲(`audio_type:"song"`)时 **MUST 下载 `accompaniment_url` 伴奏版上轨**,
97
- 不得直接用 `download_url` 原曲——人声与解说配音打架(260827 真机踩坑)。换已上轨的 BGM 文件时
96
+ 不得直接用 `download_url` 原曲——人声与解说配音会打架。换已上轨的 BGM 文件时
98
97
  注意:`audio lay` 同源幂等按文件路径判定,**换不同文件不会替换旧轨**,须先剥旧 BGM 轨再 lay。
99
- 质检照公约 §二′「三级收敛前移」(260829 拍板,替代原「渲后必跑」):L1 结构自检恒开(零成本,含**闪帧风险不可判**的前置声明);L2 画音对齐在**落轨前**跑、`--arrange-qc` 用户可选且按帧计费;L3 渲后终检**不再逼渲**,本来就要本地渲交付时随渲附带。跑过的质检结论一律随交付呈现。
98
+ 质检按公约「三级收敛」执行:L1 结构自检恒开;L2 画音对齐在落轨前可选运行;L3 渲后终检随本来就要交付的本地渲附带,不为质检单独逼渲。跑过的质检结论随交付呈现。
100
99
  字卡走 MG 临场泛化(公约 §三):店名条/菜名条/价格条为主,风格锚在这家店的气质上。
101
100
 
102
101
  ## 五、检查点①(拍板停一次;铺画面前另有一次用量确认)
103
102
 
104
103
  稿件(含引用窗口)+ 出处对照表(自备稿时)+ 画幅 + 音色(试听)+ BGM(试听)+ 字幕样式,
105
- 一屏一次拍板;**此前零计费动作**(这条仍成立)。
104
+ 一屏一次拍板;在确认前不发起计费动作。
106
105
 
107
- > ⟲ **2026-08-31 订正**:本节原写「必停一次」。本地素材的编排自该日起默认走云端并按
108
- > 「编排量」计费,`matrix lay` **跑前会再停一次**报预估——那次不是拍板,是花费确认。
106
+ > 本地素材的编排缺省走云端并按编排量计费。`matrix lay` 跑前必须报告预估用量,取得同意后才能带 `--yes`。
109
107
  > 详见 `gtrk-narration` 正本 §六 的 agent 纪律(先报用量拿同意,再带 `--yes`)。
110
108
 
111
109
  ## 六、个性化出路(垂类示例定位)
@@ -17,7 +17,7 @@ description: 直播切片图纸——把一场数小时的直播回放,拆成
17
17
 
18
18
  - ❌ 不去水印、❌ 不去烧录贴片、❌ 不做画面清理美化、❌ 不调色不裁切
19
19
  - 源片长什么样,切片就长什么样。要不要动那些元素,是**用户在客户端二次编辑时的决定**,不是你的。
20
- - 分段所需的时间轴裁切**必须用 `ffmpeg -c copy` 流拷贝**(不重编码)——既贯彻本条,也快得多(实测 40 分钟段 **12.5 秒**切完,重编码要几十分钟)。
20
+ - 分段所需的时间轴裁切**必须用 `ffmpeg -c copy` 流拷贝**(不重编码)。
21
21
 
22
22
  > **用户只要能直接发的成片、不打算再剪?** 那不是本图纸的活,转 `gtrk tool video_long2short_pro`(精剪,整片上传、云端渲染、计费约两倍)。判断句同 `gtrk-long2short`:**「剪完还要不要再动?」要动 → 留这儿;不动 → 转精剪。**
23
23
 
@@ -42,21 +42,21 @@ description: 直播切片图纸——把一场数小时的直播回放,拆成
42
42
  > ② 与 `/gtrk-long2short`、`/gtrk-tools` 精剪开工的「多人同框」一问同口径:是就开、不确定按否;
43
43
  > 分屏单段最长 8 秒、每条最多 3 处。
44
44
 
45
- > ⟲ **推荐面(公约 §三‴)**:问答收口时多问一句「要不要看看第三方 skill?」,不列名;题材命中场景触发词才多 1–2 句定向推(`gtrk skills recommend --scene <id> --json` 取 tier 最高者,各给一句为什么),用户追问才展开完整块;轻提加定向合计不超三句、同会话同场景只推一次、答「不要」零动作。
45
+ > **推荐面(公约 §三‴)**:问答收口时可问一句「要不要看看第三方 skill?」;题材命中场景触发词才定向推荐,读取 `gtrk skills recommend --scene <id> --json`;用户拒绝后不执行推荐动作。
46
46
 
47
47
  ## 二、分段规约(本图纸的骨干)
48
48
 
49
- ### 两道闸(都是实测,不是推算)
49
+ ### 两道闸
50
50
 
51
51
  | | 闸 | 值 | 命中时的表现 |
52
52
  |---|---|---|---|
53
53
  | **闸一** | 服务端媒体探测层全局业务限制 | **2 小时** | **建单期直接拒**:`6019 媒体时长无效或超过业务限制`。**任务不创建、不计费** |
54
54
  | **闸二** | CLI 轮询墙钟 | **60 分钟** | 轮询超时报错(任务在云端照常跑完,可取回) |
55
55
 
56
- **处理耗时 / 源时长比值必须按分屏与否分档**(同为 40 分钟源的实测):
56
+ **处理耗时 / 源时长比值按分屏与否分档**:
57
57
 
58
- - **不开分屏**:3.7 分钟 = **0.09×** ⇒ 墙钟几乎不构成约束
59
- - **开分屏**:21.0 分钟 = **0.53×** ⇒ 分屏是墙钟压力的**主要来源**
58
+ - **不开分屏**:墙钟压力较小;
59
+ - **开分屏**:处理耗时明显增加,优先按较短段切分。
60
60
 
61
61
  ⇒ **单段 MUST ≤ 2h(硬拒线),开分屏时 SHOULD ≤ 48min(墙钟线)。**
62
62
 
@@ -75,7 +75,7 @@ ffmpeg -ss 3600 -t 2400 -i "<直播回放>" -c copy "<工作区>/inputs/<名>_se
75
75
 
76
76
  ### ⚠️ 两条必须在动手前告诉用户
77
77
 
78
- 1. **切段是新文件,工程指向它、不指向原片。** 逐 clip 工程的 `materials[].path` 实指切段(已实测)⇒ **用户删掉切段文件,工程就素材脱机**。让他把 `inputs/` 留着。
78
+ 1. **切段是新文件,工程指向它、不指向原片。** 逐 clip 工程的 `materials[].path` 实指切段;用户删掉切段文件,工程就素材脱机。让他把 `inputs/` 留着。
79
79
  2. **超过 2 小时整条提交会被直接拒**,不是慢、是拒。好消息是零计费(不进队列),但会白抽白传一个大文件——所以先分段。
80
80
 
81
81
  ### 超时兜底
@@ -121,7 +121,7 @@ gtrk oralcut-result <task_id> --out <目录> # task_id 在产物根 task.json
121
121
  > **判断分屏是否真的生效,MUST 看产物证据,MUST NOT 按「没报错」推定。**
122
122
  > 证据 = `report.json` 的 `split_manifest` 条数 + 毛片旁 `split_screen/` 目录里的素材(可抽帧看)。
123
123
 
124
- **分屏在子轨、主轨原片可回退**(2026-08-27 起的服务端形态):分屏素材落在独立子轨上叠放,
124
+ **分屏在子轨、主轨原片可回退**:分屏素材落在独立子轨上叠放,
125
125
  主轨的原片全程完整在场——用户对某段分屏不满意时,在客户端隐藏/删除子轨该段即回退原片,
126
126
  零重跑。交付时把这条回退动线告诉用户。⚠️ 连带边界:本地 `gtrk render` 是快照渲染、
127
127
  **不合成子轨**——渲出来没有分屏画面;要分屏成片走客户端出片(或剪映导出)。
@@ -129,7 +129,7 @@ gtrk oralcut-result <task_id> --out <目录> # task_id 在产物根 task.json
129
129
  其余参数配方:
130
130
 
131
131
  - `--language` **必填**,缺失在上传前就报错(省一次白上传)
132
- - 竖屏直播源(实测四条靶片全是 9:16)**无需转比例**,`--output-size` 缺省即可
132
+ - 竖屏直播源通常**无需转比例**,`--output-size` 缺省即可
133
133
  - `--duration-pref` / `--max-clip-sec` 按投放平台口径给档
134
134
  - `--subtitle-out` 产逐 clip **独立 `.srt`**(不烧进画面——符合粗剪工程定位)
135
135
 
@@ -145,7 +145,7 @@ gtrk oralcut-result <task_id> --out <目录> # task_id 在产物根 task.json
145
145
  | **时效信息贴片**(价格 / 截止日期) | 切片再发布时是**过期信息** | 额外点一句,让用户自行判断是否适合再发 |
146
146
  | **无叠加** | — | 不必提 |
147
147
 
148
- > 实测参照:一条 3.4 小时讲座直播的标题贴片 **30 秒处与 9878 秒处逐字相同**(全场不变),而同段切出的 clip 主题与它完全无关;一条带货直播的分屏素材里价格贴片「安心退 / 245」原样带出。
148
+ > 贴片可能贯穿全场且与切片主题无关,也可能包含会过期的价格或日期;交付时按实际画面如实告知。
149
149
 
150
150
  **这一节不含任何去除动作。** 用户问「能不能去掉」→ 告诉他这超出本图纸射程,可在客户端二次编辑时自行处理。
151
151
 
@@ -158,14 +158,9 @@ gtrk oralcut-result <task_id> --out <目录> # task_id 在产物根 task.json
158
158
  - **格式**:B站式 `.xml` 是 `<d p="秒数,…">用户:文本</d>`,**`p` 首字段即距开播秒数 = 源片时码**;平台导出 `.txt` 通常是时钟列。归一化成「秒 + 文本 + 用户」三元组即可。
159
159
  - **两个信号**:**复述密度**(同一句话被多个用户在短窗口内重复——观众自发复述的往往就是金句)、**刷屏密度**(互动符号瞬时峰值)。
160
160
 
161
- ### ⚠️ 别高估它:实测弹幕对「选哪些段」几乎没有增量
161
+ ### ⚠️ 别高估它:弹幕对「选哪些段」通常只是辅助
162
162
 
163
- 一次判别性实测(40 分钟段、6 个复述热点、8 条选出的 clip):
164
-
165
- - **6/6 个热点全部落在选出的 clip 内**,而 clip 只占该段 **48%** 的时长
166
- - 纯随机情况下全中的概率约 1.2% ⇒ **服务端的语义选段本来就和观众投票高度同向**
167
-
168
- **结论**:弹幕**验证**了选段是对的,但**没提供选段之外的新信息**——它想告诉你的,LLM 基本已经选出来了。所以:
163
+ **结论**:弹幕主要用于验证选段、提取观众原句标题和微调边界,不是必需输入:
169
164
 
170
165
  - ❌ **不要**为了「更准地选段」去要用户补弹幕(没有就没有,主干照跑)
171
166
  - ✅ 它真正剩下的价值是 **取候选标题原句**:观众自发复述出来的那句话(如「大脑不接受否定」被 15 人复述),
@@ -205,22 +200,18 @@ gtrk oralcut-result <task_id> --out <目录> # task_id 在产物根 task.json
205
200
  ## 八、计费与预算速查(跑前转述给用户)
206
201
 
207
202
  - 计费基 = **上传物音频流时长**(≈源片时长),**2 积分/分钟**。
208
- - **分段不改总量**:切段总时长≈原片,所以分不分段花的钱差不多,分段买的是「能跑通」和「粒度更细的选题清单」。
209
- - 实算示例:40 分钟段 = 80 积分(实测 confirmed);一场 3.4 小时讲座 ≈ 410 积分。
203
+ - **分段不改总量**:切段总时长≈原片,所以分不分段花的钱差不多,分段买的是「能跑通」和「粒度更细的选题清单」。费用以实时价格和音频时长为准。
210
204
  - **建单期被拒是零计费的**(如超 2h 硬闸)——试探性提交不会烧钱,但会白传一个大文件。
211
- - **任务失败会自动退款**(实测失败单的计费记录状态为 `refunded`)⇒ 遇到 §九 那类瞬时故障
212
- **放心重跑,不会重复扣费**。跟用户解释时可以直接这么说。
205
+ - **任务失败按服务端回执处理**;只有回执明确退款时才向用户说明可重跑。
213
206
  - 运行时 CLI 自动查实时价格并打印,**把提示原样转述**;长片先向用户确认再跑。
214
207
 
215
208
  ## 九、排错
216
209
 
217
- - **`超过最大重试次数`(本线最常见的失败形态,务必先看这条)** → 这句是**通用兜底文案、不是真因**。
218
- 实测多次独立观测,真因都是**外部 ASR 供应商的查询接口失败**(不是你的参数错,通常也不是源片格式问题)。
210
+ - **`超过最大重试次数`** → 这句是**通用兜底文案**;优先检查外部 ASR 查询接口状态。
219
211
  - **判据**:失败很快(1–2 分钟内就报,而正常 40 分钟段要跑 4–20 分钟)⇒ 卡在开头的转写阶段。
220
- - **处置:先原样重跑一次**。**但要知道重试不保证成功**——实测存在**同一段连续两次都失败**的情况,
221
- 说明该故障**未必是瞬时的**,可能与具体音频内容相关。
212
+ - **处置:先原样重跑一次**。若仍失败,说明故障可能不是瞬时的。
222
213
  - **重试仍失败时,别死磕同一段**:换个切点重切该时段(边界挪几分钟)、或先跑其他段把能出的先出了,
223
- 把这一段单独挂起问客服/换时间再试。**MUST NOT 反复重跑**——虽然失败会退款,但每次都要等。
214
+ 把这一段单独挂起问客服/换时间再试。**MUST NOT 反复重跑**——每次都会重新等待,是否退款以回执为准。
224
215
  - **别改参数、别一味切更短**——那会掩盖问题;先确认是不是 ASR 侧的事。
225
216
  - 分段跑的好处在这里体现:**只需处理失败的那一段**,其余段的成果不受影响。
226
217
  - **`6019 媒体时长无效或超过业务限制`** → 单次提交超服务端 2 小时硬闸。**分段**,别重试。
@@ -22,7 +22,7 @@ description: 长剪短闭环——把一条长视频(课程/直播/播客/访
22
22
  > **⚠️ 开工再问一句:素材是不是多人同框(对谈 / 访谈 / 多人直播)?** 与 `/gtrk-tools` 精剪同一问、同一措辞,
23
23
  > 和上一问**一起问、一次答完**,MUST NOT 代猜。
24
24
  > 答「是」⇒ 带 `--split-screen`,**并在同一句里告知**:本地会改传一份压过的 720p 代理(否则只传抽出的音频、
25
- > 毛片不出本地),处理时间明显变长(实测 0.09× → 0.53× 源时长);分屏素材落毛片旁 `split_screen/`。
25
+ > 毛片不出本地),处理时间明显变长;分屏素材落毛片旁 `split_screen/`。
26
26
  > 答「否 / 不确定」⇒ 不带(「不确定」按「否」)。分屏是点缀:单段最长 8 秒、每条最多 3 处,落在子轨、
27
27
  > 不合心意在客户端隐藏该段即回退原片。
28
28
 
@@ -71,7 +71,7 @@ gtrk long2short "D:/毛片/对谈.mp4" --language zh-CN --split-screen --split-o
71
71
  切段用流拷贝、秒级完成、不重编码:`ffmpeg -ss 0 -t 2400 -i "<源片>" -c copy "<源片名>_seg01.mp4"`。
72
72
  各段产出的 clip 事后汇总成一张选题清单即可。**分段是常态动作,不是降级方案**。
73
73
  - 云端处理时长随片长涨(ASR+选段),CLI 轮询墙钟 60 分钟;超时不丢——产物根 `task.json` 里有 `task_id` 可恢复。
74
- 这也是 40 分钟/段的由来:墙钟 60min ÷ 开分屏实测耗时比上界 1.25× ≈ 48min,取 40 留余量。
74
+ 40 分钟/段是稳妥起步值;若分屏段仍接近墙钟上限,再继续缩短分段。
75
75
 
76
76
  ## 产物(跑完把这些讲给用户)
77
77