Refine contact sheet sampling and metadata

This commit is contained in:
Stu Leak 2026-01-03 23:26:15 -05:00
parent a44f612346
commit 658331cd67

View File

@ -373,30 +373,34 @@ func (g *Generator) generateContactSheet(ctx context.Context, config Config, dur
totalThumbs = config.Count
}
// Calculate timestamps
tempConfig := config
tempConfig.Count = totalThumbs
tempConfig.Interval = 0
timestamps := g.calculateTimestamps(tempConfig, duration)
// Build select filter using timestamps (more reliable than frame numbers)
// Use gte(t,timestamp) approach to select frames closest to target times
selectFilter := "select='"
for i, ts := range timestamps {
if i > 0 {
selectFilter += "+"
}
// Select frame at or after this timestamp, limiting to one frame per timestamp
selectFilter += fmt.Sprintf("gte(t\\,%.2f)*lt(t\\,%.2f)", ts, ts+0.1)
startTime := config.StartOffset
endTime := duration - config.EndOffset
if endTime <= startTime {
endTime = duration
}
selectFilter += "'"
availableDuration := endTime - startTime
if availableDuration <= 0 {
availableDuration = duration
}
sampleFPS := float64(totalThumbs) / availableDuration
if sampleFPS <= 0 {
sampleFPS = 0.01
}
// Build select filter using trim + fps to evenly sample across duration
selectFilter := fmt.Sprintf("trim=start=%.2f:end=%.2f,fps=%.6f,setpts=PTS-STARTPTS+%.2f/TB",
startTime,
endTime,
sampleFPS,
startTime,
)
baseName := strings.TrimSuffix(filepath.Base(config.VideoPath), filepath.Ext(config.VideoPath))
outputPath := filepath.Join(config.OutputDir, fmt.Sprintf("%s_contact_sheet.%s", baseName, config.Format))
// Build tile filter with padding between thumbnails
padding := 8 // Pixels of padding between each thumbnail
tileFilter := fmt.Sprintf("%s,setpts=N/TB,tile=%dx%d:padding=%d", g.buildThumbFilter(thumbWidth, thumbHeight, config.ShowTimestamp), config.Columns, config.Rows, padding)
tileFilter := fmt.Sprintf("%s,tile=%dx%d:padding=%d", g.buildThumbFilter(thumbWidth, thumbHeight, config.ShowTimestamp), config.Columns, config.Rows, padding)
// Build video filter
var vfilter string
@ -455,7 +459,7 @@ func (g *Generator) buildMetadataFilter(config Config, duration float64, thumbWi
// Padding is added between tiles: (cols-1) horizontal gaps and (rows-1) vertical gaps
sheetWidth := (thumbWidth * config.Columns) + (padding * (config.Columns - 1))
sheetHeight := (thumbHeight * config.Rows) + (padding * (config.Rows - 1))
headerHeight := 100
headerHeight := 110
// Build metadata text lines
// Line 1: Filename and file size
@ -481,9 +485,9 @@ func (g *Generator) buildMetadataFilter(config Config, duration float64, thumbWi
// App background color: #0B0F1A (dark navy blue)
baseFilter := fmt.Sprintf(
"%s,%s,pad=%d:%d:0:%d:0x0B0F1A,"+
"drawtext=text='%s':fontcolor=white:fontsize=13:font='DejaVu Sans Mono':x=10:y=10,"+
"drawtext=text='%s':fontcolor=white:fontsize=12:font='DejaVu Sans Mono':x=10:y=35,"+
"drawtext=text='%s':fontcolor=white:fontsize=11:font='DejaVu Sans Mono':x=10:y=60",
"drawtext=text='%s':fontcolor=white:fontsize=15:font='DejaVu Sans Mono':x=10:y=12,"+
"drawtext=text='%s':fontcolor=white:fontsize=13:font='DejaVu Sans Mono':x=10:y=40,"+
"drawtext=text='%s':fontcolor=white:fontsize=12:font='DejaVu Sans Mono':x=10:y=68",
selectFilter,
tileFilter,
sheetWidth,
@ -499,7 +503,7 @@ func (g *Generator) buildMetadataFilter(config Config, duration float64, thumbWi
return baseFilter
}
logoScale := 84
logoScale := 68
logoFilter := fmt.Sprintf("%s[sheet];movie='%s',scale=%d:%d[logo];[sheet][logo]overlay=x=main_w-overlay_w-12:y=(%d-overlay_h)/2",
baseFilter,
escapeFilterPath(logoPath),