Fixes to gnommo
This commit is contained in:
+49
-64
@@ -28,6 +28,26 @@ from .transcriber import TranscribedWord
|
||||
# Audio trigger offset: play sound this many seconds before the marker
|
||||
AUDIO_OFFSET_SECONDS = 1.0
|
||||
|
||||
# Shorthand marker prefix → (cutout_name, layer).
|
||||
# These are the ETL source-of-truth: when a manuscript contains [vft:X],
|
||||
# that projects cutout="fullscreen" and layer="above" into videos.json for X.
|
||||
# The pause-variant entries (vftp: etc.) carry a third element "pause_narration"
|
||||
# which is a per-event property, not stored in videos.json.
|
||||
_SHORTHAND_PREFIXES: dict[str, tuple] = {
|
||||
"vft:": ("fullscreen", "above"),
|
||||
"vfb:": ("fullscreen", "below"),
|
||||
"vf2t:": ("fullscreen2", "above"),
|
||||
"vf2b:": ("fullscreen2", "below"),
|
||||
"vst:": ("square", "above"),
|
||||
"vsb:": ("square", "below"),
|
||||
"vftp:": ("fullscreen", "above"),
|
||||
"vfbp:": ("fullscreen", "below"),
|
||||
"vf2tp:": ("fullscreen2", "above"),
|
||||
"vf2bp:": ("fullscreen2", "below"),
|
||||
"vstp:": ("square", "above"),
|
||||
"vsbp:": ("square", "below"),
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarkerTiming:
|
||||
@@ -961,26 +981,14 @@ def _extract_video_events(
|
||||
]
|
||||
)
|
||||
|
||||
# Mapping from shorthand marker prefix → (implied_cutout_name, implied_layer)
|
||||
# These are the defaults; videos.json values act as a base but the marker wins.
|
||||
_SHORTHAND: dict[str, tuple[str, str]] = {
|
||||
"vft:": ("fullscreen", "above"),
|
||||
"vfb:": ("fullscreen", "below"),
|
||||
"vf2t:": ("fullscreen2", "above"),
|
||||
"vf2b:": ("fullscreen2", "below"),
|
||||
"vst:": ("square", "above"),
|
||||
"vsb:": ("square", "below"),
|
||||
"vftp:": ("fullscreen", "above", "pause_narration"),
|
||||
"vfbp:": ("fullscreen", "below", "pause_narration"),
|
||||
"vf2tp:": ("fullscreen2", "above", "pause_narration"),
|
||||
"vf2bp:": ("fullscreen2", "below", "pause_narration"),
|
||||
"vstp:": ("square", "above", "pause_narration"),
|
||||
"vsbp:": ("square", "below", "pause_narration"),
|
||||
}
|
||||
# Pause-variant prefixes — the only thing the render pass still needs from
|
||||
# shorthand markers at event-build time (pause_narration is per-event, not stored in videos.json).
|
||||
_PAUSE_PREFIXES = {"vftp:", "vfbp:", "vf2tp:", "vf2bp:", "vstp:", "vsbp:"}
|
||||
|
||||
# Collect video markers: (time, video_id, event_type, cutout_name_override, layer_override)
|
||||
# event_type is "video" (ends at next slide) or "narration" (runs to end)
|
||||
video_markers: list[tuple[float, str, str, str | None, str | None]] = []
|
||||
# Collect video markers: (time, video_id, event_type, pause_narration)
|
||||
# video_markers: (timestamp, video_id, marker_type, pause_narration)
|
||||
# cutout and layer are read from videos.json (projected there by _project_markers_to_videos)
|
||||
video_markers: list[tuple[float, str, str, bool]] = []
|
||||
|
||||
for timing in marker_timings:
|
||||
if timing.timestamp < 0:
|
||||
@@ -988,26 +996,26 @@ def _extract_video_events(
|
||||
|
||||
mid = timing.marker_id
|
||||
|
||||
# --- shorthand markers: vft/vfb/vst/vsb ---
|
||||
shorthand_match = next((p for p in _SHORTHAND if mid.startswith(p)), None)
|
||||
# --- shorthand markers (vft:/vfb:/vst:/vsb: and pause variants) ---
|
||||
shorthand_match = next((p for p in _SHORTHAND_PREFIXES if mid.startswith(p)), None)
|
||||
if shorthand_match:
|
||||
video_id = mid[len(shorthand_match) :]
|
||||
video_id = mid[len(shorthand_match):]
|
||||
if video_id not in videos:
|
||||
warnings.append(
|
||||
f"[{mid}] references unknown video '{video_id}' — skipped. "
|
||||
f"Add it to videos.json or remove the marker."
|
||||
)
|
||||
continue
|
||||
implied_cutout, implied_layer = _SHORTHAND[shorthand_match]
|
||||
if implied_cutout not in cutouts:
|
||||
# Validate that videos.json has the correct cutout (written by ETL)
|
||||
video_source = videos[video_id]
|
||||
if not video_source.cutout or video_source.cutout not in cutouts:
|
||||
warnings.append(
|
||||
f"[{mid}] requires cutout '{implied_cutout}' which is not defined in project config — skipped. "
|
||||
f"Available cutouts: {list(cutouts.keys())}"
|
||||
f"[{mid}] video '{video_id}' has no valid cutout in videos.json — "
|
||||
f"run render once to project values, or set cutout manually."
|
||||
)
|
||||
continue
|
||||
video_markers.append(
|
||||
(timing.timestamp, video_id, "video", implied_cutout, implied_layer)
|
||||
)
|
||||
pause_narration = shorthand_match in _PAUSE_PREFIXES
|
||||
video_markers.append((timing.timestamp, video_id, "video", pause_narration))
|
||||
continue
|
||||
|
||||
# --- legacy [video:xxx] ---
|
||||
@@ -1015,23 +1023,16 @@ def _extract_video_events(
|
||||
video_id = mid[6:]
|
||||
if video_id not in videos:
|
||||
warnings.append(
|
||||
f"[video:{video_id}] references unknown video '{video_id}' — skipped. "
|
||||
f"Add it to videos.json or remove the marker."
|
||||
f"[video:{video_id}] references unknown video '{video_id}' — skipped."
|
||||
)
|
||||
continue
|
||||
video_source = videos[video_id]
|
||||
if not video_source.cutout:
|
||||
if not video_source.cutout or video_source.cutout not in cutouts:
|
||||
warnings.append(
|
||||
f"[video:{video_id}] has no 'cutout' set in videos.json — skipped."
|
||||
f"[video:{video_id}] has no valid cutout in videos.json — skipped."
|
||||
)
|
||||
continue
|
||||
if video_source.cutout not in cutouts:
|
||||
warnings.append(
|
||||
f"[video:{video_id}] cutout '{video_source.cutout}' is not defined in project config — skipped. "
|
||||
f"Available: {list(cutouts.keys())}"
|
||||
)
|
||||
continue
|
||||
video_markers.append((timing.timestamp, video_id, "video", None, None))
|
||||
video_markers.append((timing.timestamp, video_id, "video", False))
|
||||
continue
|
||||
|
||||
# --- [narration:xxx] ---
|
||||
@@ -1039,41 +1040,25 @@ def _extract_video_events(
|
||||
video_id = mid[10:]
|
||||
if video_id not in videos:
|
||||
warnings.append(
|
||||
f"[narration:{video_id}] references unknown video '{video_id}' — skipped. "
|
||||
f"Add it to videos.json or remove the marker."
|
||||
f"[narration:{video_id}] references unknown video '{video_id}' — skipped."
|
||||
)
|
||||
continue
|
||||
video_source = videos[video_id]
|
||||
if not video_source.cutout:
|
||||
if not video_source.cutout or video_source.cutout not in cutouts:
|
||||
warnings.append(
|
||||
f"[narration:{video_id}] has no 'cutout' set in videos.json — skipped."
|
||||
f"[narration:{video_id}] has no valid cutout in videos.json — skipped."
|
||||
)
|
||||
continue
|
||||
if video_source.cutout not in cutouts:
|
||||
warnings.append(
|
||||
f"[narration:{video_id}] cutout '{video_source.cutout}' is not defined in project config — skipped. "
|
||||
f"Available: {list(cutouts.keys())}"
|
||||
)
|
||||
continue
|
||||
video_markers.append((timing.timestamp, video_id, "narration", None, None))
|
||||
video_markers.append((timing.timestamp, video_id, "narration", False))
|
||||
|
||||
events: list[VideoEvent] = []
|
||||
for (
|
||||
start_time,
|
||||
video_id,
|
||||
marker_type,
|
||||
cutout_override,
|
||||
layer_override,
|
||||
) in video_markers:
|
||||
for (start_time, video_id, marker_type, pause_narration) in video_markers:
|
||||
video_source = videos[video_id]
|
||||
|
||||
# Resolve cutout: marker override > videos.json cutout
|
||||
# (validation already ensured cutout exists — this is a safety assertion)
|
||||
cutout_name = cutout_override or video_source.cutout
|
||||
# Read cutout and layer directly from videos.json (projected by ETL)
|
||||
cutout_name = video_source.cutout
|
||||
cutout = cutouts[cutout_name]
|
||||
|
||||
# Resolve layer: marker override > videos.json layer
|
||||
layer = layer_override if layer_override is not None else video_source.layer
|
||||
layer = video_source.layer
|
||||
|
||||
end_on = video_source.end_on
|
||||
if end_on == "take" and video_source.take is not None:
|
||||
|
||||
Reference in New Issue
Block a user