@lingjingai/scriptctl 0.57.1 → 0.58.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.
- package/changes/0.58.0.md +14 -0
- package/dist/cli/register-workflows.js +17 -0
- package/dist/cli/register-workflows.js.map +1 -1
- package/dist/domain/creative-draft/compose.d.ts +18 -0
- package/dist/domain/creative-draft/compose.js +403 -0
- package/dist/domain/creative-draft/compose.js.map +1 -0
- package/dist/domain/ingest/video-serial-md.js.map +1 -1
- package/dist/domain/ingest/video-source-evolution.d.ts +4 -0
- package/dist/domain/ingest/video-source-evolution.js +0 -3
- package/dist/domain/ingest/video-source-evolution.js.map +1 -1
- package/dist/domain/ingest/video-state-evidence.d.ts +66 -0
- package/dist/domain/ingest/video-state-evidence.js +385 -0
- package/dist/domain/ingest/video-state-evidence.js.map +1 -0
- package/dist/infra/ingest/video-discovery.js +10 -4
- package/dist/infra/ingest/video-discovery.js.map +1 -1
- package/dist/infra/ingest/video-source-snapshot.d.ts +1 -1
- package/dist/infra/ingest/video-source-snapshot.js +26 -3
- package/dist/infra/ingest/video-source-snapshot.js.map +1 -1
- package/dist/infra/ingest/video-state-evidence.d.ts +14 -0
- package/dist/infra/ingest/video-state-evidence.js +140 -0
- package/dist/infra/ingest/video-state-evidence.js.map +1 -0
- package/dist/llm/config.js +1 -1
- package/dist/llm/router.js +4 -6
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/tasks/ingest/video-serial.js +7 -0
- package/dist/llm/tasks/ingest/video-serial.js.map +1 -1
- package/dist/llm/tasks/schemas.js +3 -0
- package/dist/llm/tasks/schemas.js.map +1 -1
- package/dist/usecases/ingest/generate-video-state-evidence.d.ts +23 -0
- package/dist/usecases/ingest/generate-video-state-evidence.js +124 -0
- package/dist/usecases/ingest/generate-video-state-evidence.js.map +1 -0
- package/dist/usecases/ingest/video-pipeline.d.ts +6 -1
- package/dist/usecases/ingest/video-pipeline.js +32 -10
- package/dist/usecases/ingest/video-pipeline.js.map +1 -1
- package/dist/usecases/script/compose-creative-draft.d.ts +3 -0
- package/dist/usecases/script/compose-creative-draft.js +106 -0
- package/dist/usecases/script/compose-creative-draft.js.map +1 -0
- package/package.json +1 -1
- package/skills/scriptctl/SKILL.md +30 -9
- package/skills/scriptctl/references/ingest-workflow.md +4 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { VideoEpisodeDoc } from "./video-records.js";
|
|
2
|
+
import { type RunContext } from "./video-context.js";
|
|
3
|
+
import type { VideoSourceEpisodeIdentity } from "./video-source-evolution.js";
|
|
4
|
+
import type { ScriptV4 } from "../script/v4/types.js";
|
|
5
|
+
/** `model` remains readable for version-1 compatibility; new plans never emit it. */
|
|
6
|
+
export type VideoStateEvidenceSelection = "model" | "target_anchor" | "scene_fallback";
|
|
7
|
+
export type VideoStateEvidenceFrameStatus = "pending" | "ready" | "unavailable";
|
|
8
|
+
export interface VideoStateEvidenceSample {
|
|
9
|
+
evidence_id: string;
|
|
10
|
+
episode: number;
|
|
11
|
+
episode_id: string;
|
|
12
|
+
scene_id: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
offset_ms: -1000 | 0 | 1000;
|
|
15
|
+
selection: VideoStateEvidenceSelection;
|
|
16
|
+
source_sha256: string;
|
|
17
|
+
frame_path: string;
|
|
18
|
+
frame_status: VideoStateEvidenceFrameStatus;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface VideoActorStateEvidence {
|
|
22
|
+
evidence_id: string;
|
|
23
|
+
asset_kind: "actor";
|
|
24
|
+
actor_id: string;
|
|
25
|
+
actor_name: string;
|
|
26
|
+
state_id: string;
|
|
27
|
+
state_name: string;
|
|
28
|
+
samples: VideoStateEvidenceSample[];
|
|
29
|
+
}
|
|
30
|
+
export interface VideoActorIdentityEvidence {
|
|
31
|
+
evidence_id: string;
|
|
32
|
+
asset_kind: "actor";
|
|
33
|
+
actor_id: string;
|
|
34
|
+
actor_name: string;
|
|
35
|
+
samples: VideoStateEvidenceSample[];
|
|
36
|
+
}
|
|
37
|
+
export interface VideoStateEvidenceManifest {
|
|
38
|
+
version: 2;
|
|
39
|
+
generated_at: string;
|
|
40
|
+
source_sha256: string;
|
|
41
|
+
items: VideoActorStateEvidence[];
|
|
42
|
+
identities?: VideoActorIdentityEvidence[];
|
|
43
|
+
}
|
|
44
|
+
export interface PlannedVideoStateEvidenceSample extends VideoStateEvidenceSample {
|
|
45
|
+
source_path: string;
|
|
46
|
+
}
|
|
47
|
+
export interface PlannedVideoActorStateEvidence extends Omit<VideoActorStateEvidence, "samples"> {
|
|
48
|
+
samples: PlannedVideoStateEvidenceSample[];
|
|
49
|
+
}
|
|
50
|
+
export interface PlannedVideoActorIdentityEvidence extends Omit<VideoActorIdentityEvidence, "samples"> {
|
|
51
|
+
samples: PlannedVideoStateEvidenceSample[];
|
|
52
|
+
}
|
|
53
|
+
export interface VideoStateEvidencePlan {
|
|
54
|
+
version: 2;
|
|
55
|
+
source_sha256: string;
|
|
56
|
+
items: PlannedVideoActorStateEvidence[];
|
|
57
|
+
identities: PlannedVideoActorIdentityEvidence[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build evidence only after final Script v4 identities are stable. Gemini does
|
|
61
|
+
* not return any evidence fields: candidates come from final Action timestamps,
|
|
62
|
+
* visible-action text and single-person scene ranges, then ffmpeg materializes
|
|
63
|
+
* the selected frames locally.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildVideoStateEvidencePlan(docs: readonly VideoEpisodeDoc[], script: ScriptV4, context: RunContext, sources: readonly VideoSourceEpisodeIdentity[], sourceSha256: string): VideoStateEvidencePlan;
|
|
66
|
+
export declare function parseVideoEvidenceTimestamp(value: string): number | null;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { buildAliasMap } from "./video-context.js";
|
|
2
|
+
const IDENTITY_MIN_EVIDENCE_SCORE = 60;
|
|
3
|
+
const IDENTITY_BOARD_SIZE = 6;
|
|
4
|
+
const FRAME_OFFSETS_MS = [-1_000, 0, 1_000];
|
|
5
|
+
const WARDROBE_CUE = /(?:身穿|穿着|服装|造型|衣|裙|袍|衫|夹克|制服|西装|盔甲|装甲|防弹服|套装|裤|靴)/u;
|
|
6
|
+
const FRAMING_CUE = /(?:全身|半身|上半身|四分之三身|躯干|胸前|腰部)/u;
|
|
7
|
+
const BODY_CUE = /(?:站(?:在|立)|坐(?:在|下|起)|跪(?:在|下|倒)?|蹲(?:在|下)?|走(?:在|出|向)|行走|迈步|奔跑|跳起|跃起|转身|抬头|转头)/u;
|
|
8
|
+
const FACE_CUE = /(?:特写|近景|脸|面容|五官|正脸|侧脸|侧前脸|双眼|眼眶|眉头|嘴角)/u;
|
|
9
|
+
const BAD_VISUAL_CUE = /(?:背对|背影|远处|极远|模糊|运动模糊|脸被遮|面部遮挡|只露出|纯道具|空镜)/u;
|
|
10
|
+
const DETAIL_ONLY_CUE = /(?:双脚|脚部|膝盖|伤口|手部特写)/u;
|
|
11
|
+
const OFFSCREEN_SPEECH_CUE = /(?:画外音|旁白|未入镜|镜外|电话音|广播|\bO\.?S\.?\b)/iu;
|
|
12
|
+
function norm(value) {
|
|
13
|
+
return value.normalize("NFC").replace(/\s+/g, " ").trim();
|
|
14
|
+
}
|
|
15
|
+
function clockTimestampMilliseconds(value) {
|
|
16
|
+
const match = value.trim().match(/^(?:(\d{1,2}):)?(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?$/);
|
|
17
|
+
if (!match)
|
|
18
|
+
return null;
|
|
19
|
+
const hours = Number(match[1] ?? 0);
|
|
20
|
+
const minutes = Number(match[2]);
|
|
21
|
+
const seconds = Number(match[3]);
|
|
22
|
+
if (minutes >= 60 && match[1] !== undefined)
|
|
23
|
+
return null;
|
|
24
|
+
if (seconds >= 60)
|
|
25
|
+
return null;
|
|
26
|
+
const fraction = String(match[4] ?? "").padEnd(3, "0");
|
|
27
|
+
return ((hours * 60 + minutes) * 60 + seconds) * 1000 + Number(fraction || 0);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Some imported projects used `seconds:centiseconds` (for example `29:00`
|
|
31
|
+
* means 29 seconds) before Script v4 standardized on clock timestamps. Keep
|
|
32
|
+
* compatibility at the episode level so ordinary `00:33` remains 33 seconds.
|
|
33
|
+
*/
|
|
34
|
+
function legacyCentisecondTimestampMilliseconds(value) {
|
|
35
|
+
const match = value.trim().match(/^(\d+):(\d{2})$/);
|
|
36
|
+
if (!match)
|
|
37
|
+
return null;
|
|
38
|
+
return Number(match[1]) * 1_000 + Number(match[2]) * 10;
|
|
39
|
+
}
|
|
40
|
+
function timestampMilliseconds(value, mode = "clock") {
|
|
41
|
+
return mode === "legacy_centiseconds"
|
|
42
|
+
? legacyCentisecondTimestampMilliseconds(value)
|
|
43
|
+
: clockTimestampMilliseconds(value);
|
|
44
|
+
}
|
|
45
|
+
function inSourceRange(milliseconds, durationMs) {
|
|
46
|
+
if (milliseconds === null)
|
|
47
|
+
return false;
|
|
48
|
+
return durationMs === undefined || milliseconds <= durationMs;
|
|
49
|
+
}
|
|
50
|
+
function episodeTimestampMode(episode, durationMs) {
|
|
51
|
+
if (durationMs === undefined)
|
|
52
|
+
return "clock";
|
|
53
|
+
const timestamps = episode.scenes.flatMap((scene) => scene.actions.map(actionTimestamp)).filter(Boolean);
|
|
54
|
+
const clockValid = timestamps.filter((value) => inSourceRange(clockTimestampMilliseconds(value), durationMs)).length;
|
|
55
|
+
const legacyValid = timestamps.filter((value) => inSourceRange(legacyCentisecondTimestampMilliseconds(value), durationMs)).length;
|
|
56
|
+
// Ties intentionally remain standard clock timestamps. A legacy episode is
|
|
57
|
+
// selected only when the source duration proves it explains more Actions.
|
|
58
|
+
return legacyValid > clockValid ? "legacy_centiseconds" : "clock";
|
|
59
|
+
}
|
|
60
|
+
function timestampFromMilliseconds(milliseconds) {
|
|
61
|
+
const minutes = Math.floor(milliseconds / 60_000);
|
|
62
|
+
const seconds = Math.floor((milliseconds % 60_000) / 1_000);
|
|
63
|
+
const fraction = milliseconds % 1_000;
|
|
64
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}.${String(fraction).padStart(3, "0")}`;
|
|
65
|
+
}
|
|
66
|
+
function actionTimestamp(action) {
|
|
67
|
+
const value = action.extend?.timestamp;
|
|
68
|
+
return typeof value === "string" ? value.trim() : "";
|
|
69
|
+
}
|
|
70
|
+
function actorSurfaces(actor, contextAliases) {
|
|
71
|
+
const names = new Set([actor.actor_name, ...(actor.aliases ?? [])].map(norm).filter(Boolean));
|
|
72
|
+
for (const [alias, canonical] of contextAliases) {
|
|
73
|
+
if (norm(canonical) === norm(actor.actor_name))
|
|
74
|
+
names.add(norm(alias));
|
|
75
|
+
}
|
|
76
|
+
return [...names].sort((left, right) => right.length - left.length);
|
|
77
|
+
}
|
|
78
|
+
function mentionIndex(content, surfaces) {
|
|
79
|
+
const indices = surfaces.map((surface) => content.indexOf(surface)).filter((index) => index >= 0);
|
|
80
|
+
return indices.length > 0 ? Math.min(...indices) : -1;
|
|
81
|
+
}
|
|
82
|
+
function visibleNames(content, actors, surfacesByActor) {
|
|
83
|
+
return actors.filter((actor) => mentionIndex(content, surfacesByActor.get(actor.actor_id) ?? []) >= 0).length;
|
|
84
|
+
}
|
|
85
|
+
function scoreActionCandidate({ action, actor, actorCount, allActors, surfaces, surfacesByActor, stateName, }) {
|
|
86
|
+
const narrative = action.type === "action" || action.type === "transition";
|
|
87
|
+
const content = norm(narrative
|
|
88
|
+
? action.content
|
|
89
|
+
: "parenthetical" in action ? action.parenthetical ?? "" : "");
|
|
90
|
+
const directIndex = mentionIndex(content, surfaces);
|
|
91
|
+
const directSubject = directIndex >= 0 && directIndex <= 12;
|
|
92
|
+
const directMention = directIndex >= 0;
|
|
93
|
+
// For a visual contact sheet, a normal spoken line in a scene where the final
|
|
94
|
+
// Script already binds this actor + state is the best cheap proxy for a clean
|
|
95
|
+
// character shot. It is not identity proof (reverse shots still exist), so
|
|
96
|
+
// the client keeps the neighbouring frames and human annotation path. Explicit
|
|
97
|
+
// off-screen/voice-over speech is never used as a visual anchor.
|
|
98
|
+
const actorSpeaker = action.type === "dialogue" && "speaker" in action && "actor_id" in action.speaker
|
|
99
|
+
&& action.speaker.actor_id === actor.actor_id;
|
|
100
|
+
const visibleSpeaker = actorSpeaker && !OFFSCREEN_SPEECH_CUE.test(content);
|
|
101
|
+
const singleActorVisibleFallback = actorCount === 1 && ((narrative && content.length > 0)
|
|
102
|
+
|| (visibleSpeaker && content.length > 0 && (BODY_CUE.test(content) || FACE_CUE.test(content))));
|
|
103
|
+
if (!directMention && !visibleSpeaker && !singleActorVisibleFallback)
|
|
104
|
+
return null;
|
|
105
|
+
const selection = directSubject || visibleSpeaker ? "target_anchor" : "scene_fallback";
|
|
106
|
+
// Deliberately keep ordinary on-screen dialogue above even a very descriptive
|
|
107
|
+
// wardrobe Action: the scene-level state reference already establishes which
|
|
108
|
+
// outfit is active, while the speaking beat is more likely to show the person.
|
|
109
|
+
let stateScore = visibleSpeaker ? 250 : directSubject ? 65 : directMention ? 50 : 45;
|
|
110
|
+
let identityScore = visibleSpeaker ? 250 : directSubject ? 65 : directMention ? 50 : 45;
|
|
111
|
+
if (actorCount === 1) {
|
|
112
|
+
stateScore += 20;
|
|
113
|
+
identityScore += 20;
|
|
114
|
+
}
|
|
115
|
+
const normalizedState = norm(stateName);
|
|
116
|
+
if (normalizedState && normalizedState !== "default" && normalizedState !== "默认" && content.includes(normalizedState)) {
|
|
117
|
+
stateScore += 35;
|
|
118
|
+
}
|
|
119
|
+
if (WARDROBE_CUE.test(content))
|
|
120
|
+
stateScore += 30;
|
|
121
|
+
if (FRAMING_CUE.test(content)) {
|
|
122
|
+
stateScore += 25;
|
|
123
|
+
identityScore += 15;
|
|
124
|
+
}
|
|
125
|
+
else if (BODY_CUE.test(content)) {
|
|
126
|
+
stateScore += 15;
|
|
127
|
+
identityScore += 5;
|
|
128
|
+
}
|
|
129
|
+
if (FACE_CUE.test(content)) {
|
|
130
|
+
identityScore += 30;
|
|
131
|
+
if (!FRAMING_CUE.test(content))
|
|
132
|
+
stateScore -= 20;
|
|
133
|
+
}
|
|
134
|
+
if (BAD_VISUAL_CUE.test(content)) {
|
|
135
|
+
stateScore -= 70;
|
|
136
|
+
identityScore -= 70;
|
|
137
|
+
}
|
|
138
|
+
if (DETAIL_ONLY_CUE.test(content))
|
|
139
|
+
stateScore -= 40;
|
|
140
|
+
if (visibleNames(content, allActors, surfacesByActor) >= 2) {
|
|
141
|
+
stateScore -= 15;
|
|
142
|
+
identityScore -= 15;
|
|
143
|
+
}
|
|
144
|
+
return { stateScore, identityScore, selection };
|
|
145
|
+
}
|
|
146
|
+
function scoreStateCoverageFallback(action, allActors, surfacesByActor) {
|
|
147
|
+
const narrative = action.type === "action" || action.type === "transition";
|
|
148
|
+
const parenthetical = "parenthetical" in action ? norm(action.parenthetical ?? "") : "";
|
|
149
|
+
if (!narrative && OFFSCREEN_SPEECH_CUE.test(parenthetical))
|
|
150
|
+
return null;
|
|
151
|
+
const content = norm(narrative ? action.content : parenthetical);
|
|
152
|
+
// A scene-level timestamp cannot prove identity, but it is still a useful
|
|
153
|
+
// last-resort state anchor when the final scene reference binds one costume to
|
|
154
|
+
// the actor. Keep it below every actor-specific candidate so quality scoring
|
|
155
|
+
// only chooses the best frame; it must not make a referenced costume disappear.
|
|
156
|
+
let stateScore = narrative ? 0 : -40;
|
|
157
|
+
stateScore -= visibleNames(content, allActors, surfacesByActor) * 20;
|
|
158
|
+
if (WARDROBE_CUE.test(content))
|
|
159
|
+
stateScore += 10;
|
|
160
|
+
if (FRAMING_CUE.test(content))
|
|
161
|
+
stateScore += 10;
|
|
162
|
+
else if (BODY_CUE.test(content))
|
|
163
|
+
stateScore += 5;
|
|
164
|
+
if (BAD_VISUAL_CUE.test(content))
|
|
165
|
+
stateScore -= 20;
|
|
166
|
+
if (DETAIL_ONLY_CUE.test(content))
|
|
167
|
+
stateScore -= 10;
|
|
168
|
+
return { stateScore, identityScore: Number.NEGATIVE_INFINITY, selection: "scene_fallback" };
|
|
169
|
+
}
|
|
170
|
+
function effectiveScore(candidate, scoreKey, owners) {
|
|
171
|
+
const shared = (owners.get(`${candidate.episode}:${candidate.milliseconds}`)?.size ?? 0) > 1;
|
|
172
|
+
return candidate[scoreKey] - (shared ? 35 : 0);
|
|
173
|
+
}
|
|
174
|
+
function bestCandidates(candidates, scoreKey, owners, limit, minimumScore) {
|
|
175
|
+
const deduplicated = new Map();
|
|
176
|
+
for (const candidate of candidates) {
|
|
177
|
+
const key = `${candidate.episode}:${candidate.milliseconds}`;
|
|
178
|
+
const existing = deduplicated.get(key);
|
|
179
|
+
if (!existing || effectiveScore(candidate, scoreKey, owners) > effectiveScore(existing, scoreKey, owners)) {
|
|
180
|
+
deduplicated.set(key, candidate);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const ranked = [...deduplicated.values()]
|
|
184
|
+
.sort((left, right) => effectiveScore(right, scoreKey, owners) - effectiveScore(left, scoreKey, owners)
|
|
185
|
+
|| left.episode - right.episode
|
|
186
|
+
|| left.milliseconds - right.milliseconds)
|
|
187
|
+
.filter((candidate) => minimumScore === null || effectiveScore(candidate, scoreKey, owners) >= minimumScore);
|
|
188
|
+
// A useful contact sheet needs visual variety, not six near-identical frames
|
|
189
|
+
// from one shot. Prefer different scenes; only reuse a scene when its anchors
|
|
190
|
+
// are at least three seconds apart and the board still has empty cells.
|
|
191
|
+
const selected = [];
|
|
192
|
+
for (const candidate of ranked) {
|
|
193
|
+
const tooClose = selected.some((entry) => entry.episode === candidate.episode
|
|
194
|
+
&& entry.sceneId === candidate.sceneId
|
|
195
|
+
&& Math.abs(entry.milliseconds - candidate.milliseconds) < 3_000);
|
|
196
|
+
if (tooClose)
|
|
197
|
+
continue;
|
|
198
|
+
selected.push(candidate);
|
|
199
|
+
if (selected.length >= limit)
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
return selected;
|
|
203
|
+
}
|
|
204
|
+
function bestCandidate(candidates, scoreKey, owners) {
|
|
205
|
+
// State evidence is coverage-first: every actor-specific or neutral visual
|
|
206
|
+
// candidate remains eligible, while scores only determine which one wins.
|
|
207
|
+
return bestCandidates(candidates, scoreKey, owners, 1, null)[0] ?? null;
|
|
208
|
+
}
|
|
209
|
+
function timestampFilePart(timestamp) {
|
|
210
|
+
return timestamp.replace(/[^0-9]+/g, "-").replace(/^-|-$/g, "") || "unknown";
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Build evidence only after final Script v4 identities are stable. Gemini does
|
|
214
|
+
* not return any evidence fields: candidates come from final Action timestamps,
|
|
215
|
+
* visible-action text and single-person scene ranges, then ffmpeg materializes
|
|
216
|
+
* the selected frames locally.
|
|
217
|
+
*/
|
|
218
|
+
export function buildVideoStateEvidencePlan(docs, script, context, sources, sourceSha256) {
|
|
219
|
+
const sourceByEpisode = new Map(sources.map((source) => [source.episode, source]));
|
|
220
|
+
const contextAliases = buildAliasMap(context.registry);
|
|
221
|
+
const actorById = new Map(script.actors.map((actor) => [actor.actor_id, actor]));
|
|
222
|
+
const surfacesByActor = new Map(script.actors.map((actor) => [actor.actor_id, actorSurfaces(actor, contextAliases)]));
|
|
223
|
+
const appearancesByActor = new Map();
|
|
224
|
+
const identityCandidates = new Map();
|
|
225
|
+
const stateCandidates = new Map();
|
|
226
|
+
const owners = new Map();
|
|
227
|
+
for (let episodeIndex = 0; episodeIndex < script.episodes.length; episodeIndex += 1) {
|
|
228
|
+
const episode = script.episodes[episodeIndex];
|
|
229
|
+
const episodeNo = episodeIndex + 1;
|
|
230
|
+
const source = sourceByEpisode.get(episodeNo);
|
|
231
|
+
if (!source)
|
|
232
|
+
continue;
|
|
233
|
+
const timestampMode = episodeTimestampMode(episode, source.duration_ms);
|
|
234
|
+
for (let sceneIndex = 0; sceneIndex < episode.scenes.length; sceneIndex += 1) {
|
|
235
|
+
const scene = episode.scenes[sceneIndex];
|
|
236
|
+
const actors = scene.actors.map((ref) => actorById.get(ref.actor_id)).filter((actor) => Boolean(actor));
|
|
237
|
+
for (const ref of scene.actors) {
|
|
238
|
+
appearancesByActor.set(ref.actor_id, (appearancesByActor.get(ref.actor_id) ?? 0) + 1);
|
|
239
|
+
const actor = actorById.get(ref.actor_id);
|
|
240
|
+
if (!actor)
|
|
241
|
+
continue;
|
|
242
|
+
const surfaces = surfacesByActor.get(actor.actor_id) ?? [actor.actor_name];
|
|
243
|
+
const local = [];
|
|
244
|
+
const stateId = ref.state_ids.length === 1 ? ref.state_ids[0] : null;
|
|
245
|
+
const state = stateId ? actor.states.find((candidate) => candidate.state_id === stateId) : null;
|
|
246
|
+
for (const action of scene.actions) {
|
|
247
|
+
const timestamp = actionTimestamp(action);
|
|
248
|
+
const milliseconds = timestampMilliseconds(timestamp, timestampMode);
|
|
249
|
+
if (!inSourceRange(milliseconds, source.duration_ms))
|
|
250
|
+
continue;
|
|
251
|
+
const actorSpecific = scoreActionCandidate({
|
|
252
|
+
action,
|
|
253
|
+
actor,
|
|
254
|
+
actorCount: actors.length,
|
|
255
|
+
allActors: actors,
|
|
256
|
+
surfaces,
|
|
257
|
+
surfacesByActor,
|
|
258
|
+
stateName: state?.state_name ?? "default",
|
|
259
|
+
});
|
|
260
|
+
const scored = actorSpecific ?? (state
|
|
261
|
+
? scoreStateCoverageFallback(action, actors, surfacesByActor)
|
|
262
|
+
: null);
|
|
263
|
+
if (!scored)
|
|
264
|
+
continue;
|
|
265
|
+
const candidate = {
|
|
266
|
+
actorId: actor.actor_id,
|
|
267
|
+
episode: episodeNo,
|
|
268
|
+
episodeId: episode.episode_id,
|
|
269
|
+
sceneId: scene.scene_id,
|
|
270
|
+
timestamp: timestampFromMilliseconds(milliseconds),
|
|
271
|
+
milliseconds,
|
|
272
|
+
durationMs: source.duration_ms,
|
|
273
|
+
stateScore: scored.stateScore,
|
|
274
|
+
identityScore: scored.identityScore,
|
|
275
|
+
selection: scored.selection,
|
|
276
|
+
sourcePath: source.path,
|
|
277
|
+
sourceSha256: source.sha256,
|
|
278
|
+
};
|
|
279
|
+
local.push(candidate);
|
|
280
|
+
if (actorSpecific) {
|
|
281
|
+
const ownerKey = `${episodeNo}:${milliseconds}`;
|
|
282
|
+
const ownerSet = owners.get(ownerKey) ?? new Set();
|
|
283
|
+
ownerSet.add(actor.actor_id);
|
|
284
|
+
owners.set(ownerKey, ownerSet);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (local.length > 0) {
|
|
288
|
+
const identity = identityCandidates.get(actor.actor_id) ?? [];
|
|
289
|
+
identity.push(...local);
|
|
290
|
+
identityCandidates.set(actor.actor_id, identity);
|
|
291
|
+
// A scene-level state_ids set is unordered. Only a single state can be
|
|
292
|
+
// attributed to every Action in the scene without inventing chronology.
|
|
293
|
+
if (state) {
|
|
294
|
+
const key = `${actor.actor_id}:${state.state_id}`;
|
|
295
|
+
const values = stateCandidates.get(key) ?? [];
|
|
296
|
+
values.push(...local);
|
|
297
|
+
stateCandidates.set(key, values);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const compareActorPriority = (leftId, rightId) => {
|
|
304
|
+
const left = actorById.get(leftId);
|
|
305
|
+
const right = actorById.get(rightId);
|
|
306
|
+
const leftBackground = left?.importance === "background" ? 1 : 0;
|
|
307
|
+
const rightBackground = right?.importance === "background" ? 1 : 0;
|
|
308
|
+
return leftBackground - rightBackground
|
|
309
|
+
|| (appearancesByActor.get(rightId) ?? 0) - (appearancesByActor.get(leftId) ?? 0)
|
|
310
|
+
|| (left?.actor_name ?? leftId).localeCompare(right?.actor_name ?? rightId, "zh-CN");
|
|
311
|
+
};
|
|
312
|
+
const items = [...stateCandidates.entries()].flatMap(([key, values]) => {
|
|
313
|
+
const [actorId, stateId] = key.split(":");
|
|
314
|
+
const actor = actorId ? actorById.get(actorId) : undefined;
|
|
315
|
+
const state = actor?.states.find((candidate) => candidate.state_id === stateId);
|
|
316
|
+
const anchor = bestCandidate(values, "stateScore", owners);
|
|
317
|
+
if (!actor || !state || !anchor)
|
|
318
|
+
return [];
|
|
319
|
+
const evidenceId = `state:${actor.actor_id}:${state.state_id}`;
|
|
320
|
+
const samples = FRAME_OFFSETS_MS.map((offset) => {
|
|
321
|
+
const lastSeekableMillisecond = anchor.durationMs === undefined
|
|
322
|
+
? Number.POSITIVE_INFINITY
|
|
323
|
+
: Math.max(0, anchor.durationMs - 50);
|
|
324
|
+
const milliseconds = Math.min(lastSeekableMillisecond, Math.max(0, anchor.milliseconds + offset));
|
|
325
|
+
const timestamp = timestampFromMilliseconds(milliseconds);
|
|
326
|
+
return {
|
|
327
|
+
evidence_id: `${evidenceId}:${anchor.episode}:${timestamp}`,
|
|
328
|
+
episode: anchor.episode,
|
|
329
|
+
episode_id: anchor.episodeId,
|
|
330
|
+
scene_id: anchor.sceneId,
|
|
331
|
+
timestamp,
|
|
332
|
+
offset_ms: offset,
|
|
333
|
+
selection: anchor.selection,
|
|
334
|
+
source_sha256: anchor.sourceSha256,
|
|
335
|
+
source_path: anchor.sourcePath,
|
|
336
|
+
frame_path: `evidence/frames/${actor.actor_id}/${state.state_id}/ep_${String(anchor.episode).padStart(3, "0")}_${timestampFilePart(timestamp)}.jpg`,
|
|
337
|
+
frame_status: "pending",
|
|
338
|
+
};
|
|
339
|
+
}).filter((sample, index, all) => all.findIndex((candidate) => candidate.timestamp === sample.timestamp) === index);
|
|
340
|
+
return [{
|
|
341
|
+
evidence_id: evidenceId,
|
|
342
|
+
asset_kind: "actor",
|
|
343
|
+
actor_id: actor.actor_id,
|
|
344
|
+
actor_name: actor.actor_name,
|
|
345
|
+
state_id: state.state_id,
|
|
346
|
+
state_name: state.state_name,
|
|
347
|
+
samples,
|
|
348
|
+
}];
|
|
349
|
+
}).sort((left, right) => compareActorPriority(left.actor_id, right.actor_id)
|
|
350
|
+
|| left.state_name.localeCompare(right.state_name, "zh-CN"));
|
|
351
|
+
const identities = [...identityCandidates.entries()].flatMap(([actorId, values]) => {
|
|
352
|
+
const actor = actorById.get(actorId);
|
|
353
|
+
const anchors = bestCandidates(values, "identityScore", owners, IDENTITY_BOARD_SIZE, IDENTITY_MIN_EVIDENCE_SCORE);
|
|
354
|
+
if (!actor || anchors.length === 0)
|
|
355
|
+
return [];
|
|
356
|
+
const evidenceId = `identity:${actor.actor_id}`;
|
|
357
|
+
return [{
|
|
358
|
+
evidence_id: evidenceId,
|
|
359
|
+
asset_kind: "actor",
|
|
360
|
+
actor_id: actor.actor_id,
|
|
361
|
+
actor_name: actor.actor_name,
|
|
362
|
+
// Ranked, cross-scene identity/appearance frames. The client renders these
|
|
363
|
+
// as a compact 3x2 contact sheet; the first sample remains the single-frame
|
|
364
|
+
// fallback for older clients and inline hover cards.
|
|
365
|
+
samples: anchors.map((anchor) => ({
|
|
366
|
+
evidence_id: `${evidenceId}:${anchor.episode}:${anchor.timestamp}`,
|
|
367
|
+
episode: anchor.episode,
|
|
368
|
+
episode_id: anchor.episodeId,
|
|
369
|
+
scene_id: anchor.sceneId,
|
|
370
|
+
timestamp: anchor.timestamp,
|
|
371
|
+
offset_ms: 0,
|
|
372
|
+
selection: anchor.selection,
|
|
373
|
+
source_sha256: anchor.sourceSha256,
|
|
374
|
+
source_path: anchor.sourcePath,
|
|
375
|
+
frame_path: `evidence/frames/${actor.actor_id}/identity/ep_${String(anchor.episode).padStart(3, "0")}_${timestampFilePart(anchor.timestamp)}.jpg`,
|
|
376
|
+
frame_status: "pending",
|
|
377
|
+
})),
|
|
378
|
+
}];
|
|
379
|
+
}).sort((left, right) => compareActorPriority(left.actor_id, right.actor_id));
|
|
380
|
+
return { version: 2, source_sha256: sourceSha256, items, identities };
|
|
381
|
+
}
|
|
382
|
+
export function parseVideoEvidenceTimestamp(value) {
|
|
383
|
+
return timestampMilliseconds(value);
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=video-state-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-state-evidence.js","sourceRoot":"","sources":["../../../src/domain/ingest/video-state-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,13 +12,19 @@ function ext(p) {
|
|
|
12
12
|
function mimeFor(p) {
|
|
13
13
|
return VIDEO_MIME[ext(p)] ?? "application/octet-stream";
|
|
14
14
|
}
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
15
|
+
// Prefer an explicit episode marker before falling back to the first run of
|
|
16
|
+
// digits. Real series names commonly carry unrelated numbers (for example
|
|
17
|
+
// `749局-第35集.mp4`); treating the title number as the episode collapses an
|
|
18
|
+
// entire directory into one duplicate episode.
|
|
18
19
|
function episodeNumber(file) {
|
|
19
20
|
// Strip the extension first — `.mp4`/`.mpeg` carry digits that are not episode
|
|
20
21
|
// numbers.
|
|
21
|
-
const
|
|
22
|
+
const stem = path.basename(file, path.extname(file));
|
|
23
|
+
const explicit = stem.match(/第\s*0*(\d+)\s*[集话回]/u)
|
|
24
|
+
?? stem.match(/(?:^|[._\-\s])(?:ep(?:isode)?|e)\s*[._\-\s]*0*(\d+)(?=$|[._\-\s])/iu);
|
|
25
|
+
if (explicit)
|
|
26
|
+
return Number(explicit[1]);
|
|
27
|
+
const m = stem.match(/\d+/);
|
|
22
28
|
return m ? Number(m[0]) : null;
|
|
23
29
|
}
|
|
24
30
|
function discoveryError(message, received) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-discovery.js","sourceRoot":"","sources":["../../../src/infra/ingest/video-discovery.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,0CAA0C;AAE1C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAuB,MAAM,sBAAsB,CAAC;AASvE,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,0BAA0B,CAAC;AAC1D,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"video-discovery.js","sourceRoot":"","sources":["../../../src/infra/ingest/video-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { VideoSourceEpisodeIdentity } from "../../domain/ingest/video-source-evolution.js";
|
|
2
2
|
import type { VideoEpisode } from "./video-discovery.js";
|
|
3
|
-
export declare function snapshotVideoSource(episodes: VideoEpisode[]): VideoSourceEpisodeIdentity[];
|
|
3
|
+
export declare function snapshotVideoSource(episodes: VideoEpisode[], previous?: readonly VideoSourceEpisodeIdentity[], previousVerifiedAt?: string): VideoSourceEpisodeIdentity[];
|
|
4
4
|
export declare function videoSourceFingerprint(episodes: VideoSourceEpisodeIdentity[]): string;
|
|
@@ -1,21 +1,44 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import { sha256File, sha256Text } from "../../common.js";
|
|
3
|
-
export function snapshotVideoSource(episodes) {
|
|
4
|
+
export function snapshotVideoSource(episodes, previous = [], previousVerifiedAt) {
|
|
5
|
+
const priorByEpisode = new Map(previous.map((item) => [item.episode, item]));
|
|
6
|
+
const verifiedAtMs = previousVerifiedAt ? Date.parse(previousVerifiedAt) : Number.NaN;
|
|
4
7
|
return episodes.map((episode) => {
|
|
5
8
|
const absolutePath = path.resolve(episode.path);
|
|
9
|
+
const stat = fs.statSync(absolutePath);
|
|
10
|
+
const sizeBytes = stat.size;
|
|
11
|
+
const mtimeMs = Math.trunc(stat.mtimeMs);
|
|
12
|
+
const prior = priorByEpisode.get(episode.episode);
|
|
13
|
+
const unchangedByMetadata = prior
|
|
14
|
+
&& prior.size_bytes === sizeBytes
|
|
15
|
+
&& prior.mtime_ms === mtimeMs;
|
|
16
|
+
const unchangedSinceLegacyVerification = prior
|
|
17
|
+
&& prior.size_bytes === undefined
|
|
18
|
+
&& prior.mtime_ms === undefined
|
|
19
|
+
&& prior.path === absolutePath
|
|
20
|
+
&& Number.isFinite(verifiedAtMs)
|
|
21
|
+
&& stat.mtimeMs <= verifiedAtMs;
|
|
22
|
+
const sha256 = prior
|
|
23
|
+
&& prior.ext === episode.ext
|
|
24
|
+
&& prior.mime === episode.mime
|
|
25
|
+
&& (unchangedByMetadata || unchangedSinceLegacyVerification)
|
|
26
|
+
? prior.sha256
|
|
27
|
+
: sha256File(absolutePath);
|
|
6
28
|
return {
|
|
7
29
|
episode: episode.episode,
|
|
8
30
|
path: absolutePath,
|
|
9
31
|
ext: episode.ext,
|
|
10
32
|
mime: episode.mime,
|
|
11
|
-
sha256
|
|
33
|
+
sha256,
|
|
34
|
+
size_bytes: sizeBytes,
|
|
35
|
+
mtime_ms: mtimeMs,
|
|
12
36
|
};
|
|
13
37
|
});
|
|
14
38
|
}
|
|
15
39
|
export function videoSourceFingerprint(episodes) {
|
|
16
40
|
return sha256Text(JSON.stringify(episodes.map((episode) => ({
|
|
17
41
|
episode: episode.episode,
|
|
18
|
-
path: episode.path,
|
|
19
42
|
ext: episode.ext,
|
|
20
43
|
mime: episode.mime,
|
|
21
44
|
sha256: episode.sha256,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-source-snapshot.js","sourceRoot":"","sources":["../../../src/infra/ingest/video-source-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIzD,MAAM,UAAU,mBAAmB,
|
|
1
|
+
{"version":3,"file":"video-source-snapshot.js","sourceRoot":"","sources":["../../../src/infra/ingest/video-source-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIzD,MAAM,UAAU,mBAAmB,CACjC,QAAwB,EACxB,WAAkD,EAAE,EACpD,kBAA2B;IAE3B,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,YAAY,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACtF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,mBAAmB,GAAG,KAAK;eAC5B,KAAK,CAAC,UAAU,KAAK,SAAS;eAC9B,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;QAChC,MAAM,gCAAgC,GAAG,KAAK;eACzC,KAAK,CAAC,UAAU,KAAK,SAAS;eAC9B,KAAK,CAAC,QAAQ,KAAK,SAAS;eAC5B,KAAK,CAAC,IAAI,KAAK,YAAY;eAC3B,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;eAC7B,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC;QAClC,MAAM,MAAM,GAAG,KAAK;eACf,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;eACzB,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;eAC3B,CAAC,mBAAmB,IAAI,gCAAgC,CAAC;YAC5D,CAAC,CAAC,KAAK,CAAC,MAAM;YACd,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,YAAY;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM;YACN,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,OAAO;SAClB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAsC;IAC3E,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC,CAAC,CAAC,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type VideoStateEvidenceManifest, type VideoStateEvidencePlan } from "../../domain/ingest/video-state-evidence.js";
|
|
2
|
+
import type { WorkspaceStore } from "./workspace-store.js";
|
|
3
|
+
export interface FrameExtractionResult {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export type VideoFrameExtractor = (sourcePath: string, timestamp: string, outputPath: string, signal?: AbortSignal) => Promise<FrameExtractionResult>;
|
|
8
|
+
export interface VideoStateEvidenceMaterializeOptions {
|
|
9
|
+
/** Reuse a ready frame already present at the deterministic output path. */
|
|
10
|
+
reuseExistingFrames?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const extractVideoEvidenceFrame: VideoFrameExtractor;
|
|
13
|
+
/** Materialize a plan without making frame-tooling failures fatal to ingest. */
|
|
14
|
+
export declare function materializeVideoStateEvidence(store: WorkspaceStore, plan: VideoStateEvidencePlan, signal?: AbortSignal, extractor?: VideoFrameExtractor, options?: VideoStateEvidenceMaterializeOptions): Promise<VideoStateEvidenceManifest>;
|