From ebca98b46fc217fbad51c594d8cda869c74a2e79 Mon Sep 17 00:00:00 2001 From: Speck Pratt Date: Wed, 22 Apr 2026 08:03:37 -0400 Subject: [PATCH] Honor marker end times by default; skip zero-interval previews Follow-up to the initial ceiling-configurable change, incorporating product-review feedback. Two design shifts plus a read-side bug fix: - Default MaxMarkerPreviewDuration flipped from 20 to 0 (ceiling disabled). User-set end times are now honored by default; the ceiling becomes an opt-in safety for imports or untrusted data. A buried setting at its old 20s default was unlikely to be found by users hitting the silent-truncation case. - Non-positive intervals (endSeconds <= seconds) now skip preview generation entirely rather than falling back to the 20s default. A user who sets zero or negative duration either made a mistake (don't invent output) or marked a point without wanting a video (don't generate one they didn't ask for). Either way, skip with a warning log. - Fix missing ConfigGeneralResult field wiring in resolver_query_configuration.go. The schema and mutation sides were wired in the initial commit but the query read path was missed, causing the Settings UI to display Go's zero-value (0) regardless of the stored config value. Behavior change on upgrade: existing preview files remain unchanged. To refresh previews for markers whose end times were previously truncated at 20s, run Generate > Marker Previews with "Overwrite Existing" enabled. To keep the old 20s ceiling, set Max marker preview duration to 20 under Settings > System > Preview Generation. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/api/resolver_query_configuration.go | 1 + internal/manager/config/config.go | 2 +- pkg/scene/generate/marker_preview.go | 17 ++++++++++------- ui/v2.5/src/locales/en-GB.json | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/internal/api/resolver_query_configuration.go b/internal/api/resolver_query_configuration.go index cf2c0e3cc..0098c5769 100644 --- a/internal/api/resolver_query_configuration.go +++ b/internal/api/resolver_query_configuration.go @@ -104,6 +104,7 @@ func makeConfigGeneralResult() *ConfigGeneralResult { PreviewAudio: config.GetPreviewAudio(), PreviewSegments: config.GetPreviewSegments(), PreviewSegmentDuration: config.GetPreviewSegmentDuration(), + MaxMarkerPreviewDuration: config.GetMaxMarkerPreviewDuration(), PreviewExcludeStart: config.GetPreviewExcludeStart(), PreviewExcludeEnd: config.GetPreviewExcludeEnd(), PreviewPreset: config.GetPreviewPreset(), diff --git a/internal/manager/config/config.go b/internal/manager/config/config.go index 05dcf97a4..1c75f4719 100644 --- a/internal/manager/config/config.go +++ b/internal/manager/config/config.go @@ -120,7 +120,7 @@ const ( previewExcludeEndDefault = "0" MaxMarkerPreviewDuration = "max_marker_preview_duration" - maxMarkerPreviewDurationDefault = 20 + maxMarkerPreviewDurationDefault = 0 WriteImageThumbnails = "write_image_thumbnails" writeImageThumbnailsDefault = true diff --git a/pkg/scene/generate/marker_preview.go b/pkg/scene/generate/marker_preview.go index 83faec2fa..0f7e612a6 100644 --- a/pkg/scene/generate/marker_preview.go +++ b/pkg/scene/generate/marker_preview.go @@ -39,16 +39,19 @@ func (g Generator) MarkerPreviewVideo(ctx context.Context, input string, hash st // Honor the marker's explicit interval when present and positive, capped // by the configured safety ceiling. maxDuration <= 0 disables the ceiling. + // Non-positive intervals are treated as "no video wanted" and skipped — + // if the user intentionally set end = start they didn't want a preview, + // and if it's a data mistake we'd rather surface it than silently default. if endSeconds != nil { interval := *endSeconds - seconds - if interval > 0 { - if maxDuration <= 0 || interval <= float64(maxDuration) { - duration = interval - } else { - duration = float64(maxDuration) - } + if interval <= 0 { + logger.Warnf("[generator] marker at %.2fs has non-positive interval (end=%.2f); skipping video preview generation", seconds, *endSeconds) + return nil + } + if maxDuration <= 0 || interval <= float64(maxDuration) { + duration = interval } else { - logger.Warnf("[generator] marker at %.2fs has non-positive interval (end=%.2f); falling back to %ds default", seconds, *endSeconds, markerPreviewDefaultDuration) + duration = float64(maxDuration) } } diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 3cc754928..d0e044dcc 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -418,7 +418,7 @@ "include_audio_desc": "Includes audio stream when generating previews.", "include_audio_head": "Include audio", "logging": "Logging", - "max_marker_preview_duration_desc": "Ceiling (in seconds) applied to generated marker preview videos when the marker has an explicit end time. Protects against unexpectedly long previews from imports or data entry mistakes. Set to 0 to disable the ceiling and honor the marker's end time verbatim.", + "max_marker_preview_duration_desc": "Optional ceiling (in seconds) for marker preview videos with explicit end times. Default is 0 (no ceiling, the marker's end time is honored verbatim). Set a positive value to cap preview duration as a safety against imports or data entry mistakes. Markers without an end time use a fixed 20-second default, unaffected by this setting.", "max_marker_preview_duration_head": "Max marker preview duration", "maximum_streaming_transcode_size_desc": "Maximum size for transcoded streams.", "maximum_streaming_transcode_size_head": "Maximum streaming transcode size",