mirror of
https://github.com/stashapp/stash.git
synced 2026-05-09 05:05:29 +02:00
Make marker preview duration ceiling configurable
Replaces the hardcoded maxMarkerPreviewDuration = 20 constant in pkg/scene/generate/marker_preview.go with a configurable setting on ConfigGeneral, mirroring the PreviewSegments wiring pattern. - Default 20 preserves existing behavior for all installs - Positive N sets the ceiling in seconds - 0 (or any value <= 0) disables the ceiling, honoring explicit endSeconds verbatim - Adds logger.Warnf for non-positive intervals to aid diagnosis of imported or malformed marker data - Incidentally prevents zero/negative-duration ffmpeg calls that were possible under the previous logic Context: closed issue #6852 (template non-conformance), Discussion #6851 (mobile-first direction). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
22d2dbc46b
commit
e9e427772b
8 changed files with 48 additions and 7 deletions
|
|
@ -101,6 +101,8 @@ input ConfigGeneralInput {
|
|||
previewSegments: Int
|
||||
"Preview segment duration, in seconds"
|
||||
previewSegmentDuration: Float
|
||||
"Maximum duration (in seconds) for generated marker preview videos. 0 disables the ceiling."
|
||||
maxMarkerPreviewDuration: Int
|
||||
"Duration of start of video to exclude when generating previews"
|
||||
previewExcludeStart: String
|
||||
"Duration of end of video to exclude when generating previews"
|
||||
|
|
@ -239,6 +241,8 @@ type ConfigGeneralResult {
|
|||
previewSegments: Int!
|
||||
"Preview segment duration, in seconds"
|
||||
previewSegmentDuration: Float!
|
||||
"Maximum duration (in seconds) for generated marker preview videos. 0 disables the ceiling."
|
||||
maxMarkerPreviewDuration: Int!
|
||||
"Duration of start of video to exclude when generating previews"
|
||||
previewExcludeStart: String!
|
||||
"Duration of end of video to exclude when generating previews"
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input ConfigGen
|
|||
r.setConfigBool(config.PreviewAudio, input.PreviewAudio)
|
||||
r.setConfigInt(config.PreviewSegments, input.PreviewSegments)
|
||||
r.setConfigFloat(config.PreviewSegmentDuration, input.PreviewSegmentDuration)
|
||||
r.setConfigInt(config.MaxMarkerPreviewDuration, input.MaxMarkerPreviewDuration)
|
||||
r.setConfigString(config.PreviewExcludeStart, input.PreviewExcludeStart)
|
||||
r.setConfigString(config.PreviewExcludeEnd, input.PreviewExcludeEnd)
|
||||
if input.PreviewPreset != nil {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,9 @@ const (
|
|||
PreviewExcludeEnd = "preview_exclude_end"
|
||||
previewExcludeEndDefault = "0"
|
||||
|
||||
MaxMarkerPreviewDuration = "max_marker_preview_duration"
|
||||
maxMarkerPreviewDurationDefault = 20
|
||||
|
||||
WriteImageThumbnails = "write_image_thumbnails"
|
||||
writeImageThumbnailsDefault = true
|
||||
|
||||
|
|
@ -1079,6 +1082,13 @@ func (i *Config) GetTranscodeHardwareAcceleration() bool {
|
|||
return i.getBool(TranscodeHardwareAcceleration)
|
||||
}
|
||||
|
||||
// GetMaxMarkerPreviewDuration returns the ceiling in seconds applied to
|
||||
// generated marker preview videos when the marker has an explicit end time.
|
||||
// Any value <= 0 disables the ceiling, honoring the marker's end time verbatim.
|
||||
func (i *Config) GetMaxMarkerPreviewDuration() int {
|
||||
return i.getInt(MaxMarkerPreviewDuration)
|
||||
}
|
||||
|
||||
func (i *Config) GetMaxTranscodeSize() models.StreamingResolutionEnum {
|
||||
ret := i.getString(MaxTranscodeSize)
|
||||
|
||||
|
|
@ -1918,6 +1928,7 @@ func (i *Config) setDefaultValues() {
|
|||
i.setDefault(PreviewExcludeStart, previewExcludeStartDefault)
|
||||
i.setDefault(PreviewExcludeEnd, previewExcludeEndDefault)
|
||||
i.setDefault(PreviewAudio, previewAudioDefault)
|
||||
i.setDefault(MaxMarkerPreviewDuration, maxMarkerPreviewDurationDefault)
|
||||
i.setDefault(SoundOnPreview, false)
|
||||
|
||||
i.setDefault(UseCustomSpriteInterval, UseCustomSpriteIntervalDefault)
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func (t *GenerateMarkersTask) generateMarker(videoFile *models.VideoFile, scene
|
|||
g := t.generator
|
||||
|
||||
if t.VideoPreview {
|
||||
if err := g.MarkerPreviewVideo(context.TODO(), videoFile.Path, sceneHash, seconds, sceneMarker.EndSeconds, instance.Config.GetPreviewAudio()); err != nil {
|
||||
if err := g.MarkerPreviewVideo(context.TODO(), videoFile.Path, sceneHash, seconds, sceneMarker.EndSeconds, instance.Config.GetPreviewAudio(), instance.Config.GetMaxMarkerPreviewDuration()); err != nil {
|
||||
logger.Errorf("[generator] failed to generate marker video: %v", err)
|
||||
logErrorOutput(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,20 @@ import (
|
|||
|
||||
const (
|
||||
markerPreviewWidth = 640
|
||||
maxMarkerPreviewDuration = 20
|
||||
markerPreviewAudioBitrate = "64k"
|
||||
|
||||
// Fallback duration for markers that lack a usable explicit end time
|
||||
// (nil EndSeconds, or EndSeconds <= start). Not user-configurable;
|
||||
// markers wanting a specific duration should set an end time.
|
||||
markerPreviewDefaultDuration = 20
|
||||
|
||||
markerImageDuration = 5
|
||||
markerWebpFPS = 12
|
||||
|
||||
markerScreenshotQuality = 2
|
||||
)
|
||||
|
||||
func (g Generator) MarkerPreviewVideo(ctx context.Context, input string, hash string, seconds float64, endSeconds *float64, includeAudio bool) error {
|
||||
func (g Generator) MarkerPreviewVideo(ctx context.Context, input string, hash string, seconds float64, endSeconds *float64, includeAudio bool, maxDuration int) error {
|
||||
lockCtx := g.LockManager.ReadLock(ctx, input)
|
||||
defer lockCtx.Cancel()
|
||||
|
||||
|
|
@ -31,11 +35,21 @@ func (g Generator) MarkerPreviewVideo(ctx context.Context, input string, hash st
|
|||
}
|
||||
}
|
||||
|
||||
duration := float64(maxMarkerPreviewDuration)
|
||||
duration := float64(markerPreviewDefaultDuration)
|
||||
|
||||
// don't allow preview to exceed max duration
|
||||
if endSeconds != nil && *endSeconds-seconds < maxMarkerPreviewDuration {
|
||||
duration = float64(*endSeconds) - seconds
|
||||
// Honor the marker's explicit interval when present and positive, capped
|
||||
// by the configured safety ceiling. maxDuration <= 0 disables the ceiling.
|
||||
if endSeconds != nil {
|
||||
interval := *endSeconds - seconds
|
||||
if interval > 0 {
|
||||
if maxDuration <= 0 || interval <= float64(maxDuration) {
|
||||
duration = interval
|
||||
} else {
|
||||
duration = float64(maxDuration)
|
||||
}
|
||||
} else {
|
||||
logger.Warnf("[generator] marker at %.2fs has non-positive interval (end=%.2f); falling back to %ds default", seconds, *endSeconds, markerPreviewDefaultDuration)
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.generateFile(lockCtx, g.MarkerPaths, mp4Pattern, output, g.markerPreviewVideo(input, sceneMarkerOptions{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ fragment ConfigGeneralData on ConfigGeneralResult {
|
|||
previewExcludeStart
|
||||
previewExcludeEnd
|
||||
previewPreset
|
||||
maxMarkerPreviewDuration
|
||||
transcodeHardwareAcceleration
|
||||
maxTranscodeSize
|
||||
maxStreamingTranscodeSize
|
||||
|
|
|
|||
|
|
@ -425,6 +425,14 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||
return <></>;
|
||||
}}
|
||||
/>
|
||||
|
||||
<NumberSetting
|
||||
id="max-marker-preview-duration"
|
||||
headingID="config.general.max_marker_preview_duration_head"
|
||||
subHeadingID="config.general.max_marker_preview_duration_desc"
|
||||
value={general.maxMarkerPreviewDuration ?? 20}
|
||||
onChange={(v) => saveGeneral({ maxMarkerPreviewDuration: v })}
|
||||
/>
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection headingID="config.general.sprite_generation_head">
|
||||
|
|
|
|||
|
|
@ -418,6 +418,8 @@
|
|||
"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_head": "Max marker preview duration",
|
||||
"maximum_streaming_transcode_size_desc": "Maximum size for transcoded streams.",
|
||||
"maximum_streaming_transcode_size_head": "Maximum streaming transcode size",
|
||||
"maximum_transcode_size_desc": "Maximum size for generated transcodes.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue