Refine contact sheet sampling and metadata
This commit is contained in:
parent
a44f612346
commit
658331cd67
|
|
@ -373,30 +373,34 @@ func (g *Generator) generateContactSheet(ctx context.Context, config Config, dur
|
||||||
totalThumbs = config.Count
|
totalThumbs = config.Count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate timestamps
|
startTime := config.StartOffset
|
||||||
tempConfig := config
|
endTime := duration - config.EndOffset
|
||||||
tempConfig.Count = totalThumbs
|
if endTime <= startTime {
|
||||||
tempConfig.Interval = 0
|
endTime = duration
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
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))
|
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))
|
outputPath := filepath.Join(config.OutputDir, fmt.Sprintf("%s_contact_sheet.%s", baseName, config.Format))
|
||||||
|
|
||||||
// Build tile filter with padding between thumbnails
|
// Build tile filter with padding between thumbnails
|
||||||
padding := 8 // Pixels of padding between each thumbnail
|
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
|
// Build video filter
|
||||||
var vfilter string
|
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
|
// Padding is added between tiles: (cols-1) horizontal gaps and (rows-1) vertical gaps
|
||||||
sheetWidth := (thumbWidth * config.Columns) + (padding * (config.Columns - 1))
|
sheetWidth := (thumbWidth * config.Columns) + (padding * (config.Columns - 1))
|
||||||
sheetHeight := (thumbHeight * config.Rows) + (padding * (config.Rows - 1))
|
sheetHeight := (thumbHeight * config.Rows) + (padding * (config.Rows - 1))
|
||||||
headerHeight := 100
|
headerHeight := 110
|
||||||
|
|
||||||
// Build metadata text lines
|
// Build metadata text lines
|
||||||
// Line 1: Filename and file size
|
// 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)
|
// App background color: #0B0F1A (dark navy blue)
|
||||||
baseFilter := fmt.Sprintf(
|
baseFilter := fmt.Sprintf(
|
||||||
"%s,%s,pad=%d:%d:0:%d:0x0B0F1A,"+
|
"%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=15:font='DejaVu Sans Mono':x=10:y=12,"+
|
||||||
"drawtext=text='%s':fontcolor=white:fontsize=12:font='DejaVu Sans Mono':x=10:y=35,"+
|
"drawtext=text='%s':fontcolor=white:fontsize=13:font='DejaVu Sans Mono':x=10:y=40,"+
|
||||||
"drawtext=text='%s':fontcolor=white:fontsize=11:font='DejaVu Sans Mono':x=10:y=60",
|
"drawtext=text='%s':fontcolor=white:fontsize=12:font='DejaVu Sans Mono':x=10:y=68",
|
||||||
selectFilter,
|
selectFilter,
|
||||||
tileFilter,
|
tileFilter,
|
||||||
sheetWidth,
|
sheetWidth,
|
||||||
|
|
@ -499,7 +503,7 @@ func (g *Generator) buildMetadataFilter(config Config, duration float64, thumbWi
|
||||||
return baseFilter
|
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",
|
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,
|
baseFilter,
|
||||||
escapeFilterPath(logoPath),
|
escapeFilterPath(logoPath),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user