Compare commits
3 Commits
db35300723
...
a40f7ad795
| Author | SHA1 | Date | |
|---|---|---|---|
| a40f7ad795 | |||
| 37fa9d1a5c | |||
| 701e2592ee |
|
|
@ -234,7 +234,7 @@ func (g *Generator) generateIndividual(ctx context.Context, config Config, durat
|
|||
seconds := int(ts) % 60
|
||||
timeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
|
||||
|
||||
drawTextFilter := fmt.Sprintf("scale=%d:%d,drawtext=text='%s':fontcolor=white:fontsize=20:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=h-th-10",
|
||||
drawTextFilter := fmt.Sprintf("scale=%d:%d,drawtext=text='%s':fontcolor=white:fontsize=20:font='DejaVu Sans Mono':box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=h-th-10",
|
||||
thumbWidth, thumbHeight, timeStr)
|
||||
|
||||
// Replace scale filter with combined filter
|
||||
|
|
@ -363,12 +363,12 @@ func (g *Generator) buildMetadataFilter(config Config, duration float64, thumbWi
|
|||
// Create filter that:
|
||||
// 1. Generates contact sheet from selected frames
|
||||
// 2. Creates a blank header area
|
||||
// 3. Draws metadata text on header
|
||||
// 3. Draws metadata text on header (using monospace font)
|
||||
// 4. Stacks header on top of contact sheet
|
||||
filter := fmt.Sprintf(
|
||||
"%s,%s,pad=%d:%d:0:%d:black,"+
|
||||
"drawtext=text='%s':fontcolor=white:fontsize=14:x=10:y=10,"+
|
||||
"drawtext=text='%s':fontcolor=white:fontsize=12:x=10:y=35",
|
||||
"drawtext=text='%s':fontcolor=white:fontsize=14:font='DejaVu Sans Mono':x=10:y=10,"+
|
||||
"drawtext=text='%s':fontcolor=white:fontsize=12:font='DejaVu Sans Mono':x=10:y=35",
|
||||
selectFilter,
|
||||
tileFilter,
|
||||
sheetWidth,
|
||||
|
|
|
|||
111
main.go
111
main.go
|
|
@ -9504,12 +9504,16 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
state.thumbRows = 6 // 4x6 = 24 thumbnails
|
||||
}
|
||||
|
||||
// File label
|
||||
// File label and video preview
|
||||
fileLabel := widget.NewLabel("No file loaded")
|
||||
fileLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||||
|
||||
var videoContainer fyne.CanvasObject
|
||||
if state.thumbFile != nil {
|
||||
fileLabel.SetText(fmt.Sprintf("File: %s", filepath.Base(state.thumbFile.Path)))
|
||||
videoContainer = buildVideoPane(state, fyne.NewSize(640, 360), state.thumbFile, nil)
|
||||
} else {
|
||||
videoContainer = container.NewCenter(widget.NewLabel("No video loaded"))
|
||||
}
|
||||
|
||||
// Load button
|
||||
|
|
@ -9540,26 +9544,6 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
})
|
||||
clearBtn.Importance = widget.LowImportance
|
||||
|
||||
// Thumbnail count slider
|
||||
countLabel := widget.NewLabel(fmt.Sprintf("Thumbnail Count: %d", state.thumbCount))
|
||||
countSlider := widget.NewSlider(3, 50)
|
||||
countSlider.Value = float64(state.thumbCount)
|
||||
countSlider.Step = 1
|
||||
countSlider.OnChanged = func(val float64) {
|
||||
state.thumbCount = int(val)
|
||||
countLabel.SetText(fmt.Sprintf("Thumbnail Count: %d", int(val)))
|
||||
}
|
||||
|
||||
// Thumbnail width slider
|
||||
widthLabel := widget.NewLabel(fmt.Sprintf("Thumbnail Width: %d px", state.thumbWidth))
|
||||
widthSlider := widget.NewSlider(160, 640)
|
||||
widthSlider.Value = float64(state.thumbWidth)
|
||||
widthSlider.Step = 32
|
||||
widthSlider.OnChanged = func(val float64) {
|
||||
state.thumbWidth = int(val)
|
||||
widthLabel.SetText(fmt.Sprintf("Thumbnail Width: %d px", int(val)))
|
||||
}
|
||||
|
||||
// Contact sheet checkbox
|
||||
contactSheetCheck := widget.NewCheck("Generate Contact Sheet (single image)", func(checked bool) {
|
||||
state.thumbContactSheet = checked
|
||||
|
|
@ -9567,9 +9551,10 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
})
|
||||
contactSheetCheck.Checked = state.thumbContactSheet
|
||||
|
||||
// Contact sheet grid options (only show if contact sheet is enabled)
|
||||
var gridOptions fyne.CanvasObject
|
||||
// Conditional settings based on contact sheet mode
|
||||
var settingsOptions fyne.CanvasObject
|
||||
if state.thumbContactSheet {
|
||||
// Contact sheet mode: show columns and rows
|
||||
colLabel := widget.NewLabel(fmt.Sprintf("Columns: %d", state.thumbColumns))
|
||||
colSlider := widget.NewSlider(2, 12)
|
||||
colSlider.Value = float64(state.thumbColumns)
|
||||
|
|
@ -9588,16 +9573,47 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
rowLabel.SetText(fmt.Sprintf("Rows: %d", int(val)))
|
||||
}
|
||||
|
||||
gridOptions = container.NewVBox(
|
||||
totalThumbs := state.thumbColumns * state.thumbRows
|
||||
totalLabel := widget.NewLabel(fmt.Sprintf("Total thumbnails: %d", totalThumbs))
|
||||
totalLabel.TextStyle = fyne.TextStyle{Italic: true}
|
||||
|
||||
settingsOptions = container.NewVBox(
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Contact Sheet Grid:"),
|
||||
colLabel,
|
||||
colSlider,
|
||||
rowLabel,
|
||||
rowSlider,
|
||||
totalLabel,
|
||||
)
|
||||
} else {
|
||||
gridOptions = container.NewVBox()
|
||||
// Individual thumbnails mode: show count and width
|
||||
countLabel := widget.NewLabel(fmt.Sprintf("Thumbnail Count: %d", state.thumbCount))
|
||||
countSlider := widget.NewSlider(3, 50)
|
||||
countSlider.Value = float64(state.thumbCount)
|
||||
countSlider.Step = 1
|
||||
countSlider.OnChanged = func(val float64) {
|
||||
state.thumbCount = int(val)
|
||||
countLabel.SetText(fmt.Sprintf("Thumbnail Count: %d", int(val)))
|
||||
}
|
||||
|
||||
widthLabel := widget.NewLabel(fmt.Sprintf("Thumbnail Width: %d px", state.thumbWidth))
|
||||
widthSlider := widget.NewSlider(160, 640)
|
||||
widthSlider.Value = float64(state.thumbWidth)
|
||||
widthSlider.Step = 32
|
||||
widthSlider.OnChanged = func(val float64) {
|
||||
state.thumbWidth = int(val)
|
||||
widthLabel.SetText(fmt.Sprintf("Thumbnail Width: %d px", int(val)))
|
||||
}
|
||||
|
||||
settingsOptions = container.NewVBox(
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Individual Thumbnails:"),
|
||||
countLabel,
|
||||
countSlider,
|
||||
widthLabel,
|
||||
widthSlider,
|
||||
)
|
||||
}
|
||||
|
||||
// Generate button
|
||||
|
|
@ -9611,15 +9627,30 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
state.showThumbView()
|
||||
|
||||
go func() {
|
||||
// Create temp directory for thumbnails
|
||||
outputDir := filepath.Join(os.TempDir(), fmt.Sprintf("videotools_thumbs_%d", time.Now().Unix()))
|
||||
// Create output directory in same folder as video
|
||||
videoDir := filepath.Dir(state.thumbFile.Path)
|
||||
videoBaseName := strings.TrimSuffix(filepath.Base(state.thumbFile.Path), filepath.Ext(state.thumbFile.Path))
|
||||
outputDir := filepath.Join(videoDir, fmt.Sprintf("%s_thumbnails", videoBaseName))
|
||||
|
||||
generator := thumbnail.NewGenerator(platformConfig.FFmpegPath)
|
||||
|
||||
// Configure based on mode
|
||||
var count, width int
|
||||
if state.thumbContactSheet {
|
||||
// Contact sheet: count is determined by grid, use default width
|
||||
count = state.thumbColumns * state.thumbRows
|
||||
width = 320 // Fixed width for contact sheets
|
||||
} else {
|
||||
// Individual thumbnails: use user settings
|
||||
count = state.thumbCount
|
||||
width = state.thumbWidth
|
||||
}
|
||||
|
||||
config := thumbnail.Config{
|
||||
VideoPath: state.thumbFile.Path,
|
||||
OutputDir: outputDir,
|
||||
Count: state.thumbCount,
|
||||
Width: state.thumbWidth,
|
||||
Count: count,
|
||||
Width: width,
|
||||
Format: "jpg",
|
||||
Quality: 85,
|
||||
ContactSheet: state.thumbContactSheet,
|
||||
|
|
@ -9681,24 +9712,30 @@ func buildThumbView(state *appState) fyne.CanvasObject {
|
|||
settingsPanel := container.NewVBox(
|
||||
widget.NewLabel("Settings:"),
|
||||
widget.NewSeparator(),
|
||||
countLabel,
|
||||
countSlider,
|
||||
widthLabel,
|
||||
widthSlider,
|
||||
widget.NewSeparator(),
|
||||
contactSheetCheck,
|
||||
gridOptions,
|
||||
settingsOptions,
|
||||
widget.NewSeparator(),
|
||||
generateBtn,
|
||||
)
|
||||
|
||||
// Main content
|
||||
// Main content - split layout with preview on left, settings on right
|
||||
leftColumn := container.NewVBox(
|
||||
videoContainer,
|
||||
)
|
||||
|
||||
rightColumn := container.NewVBox(
|
||||
settingsPanel,
|
||||
)
|
||||
|
||||
mainContent := container.NewHSplit(leftColumn, rightColumn)
|
||||
mainContent.Offset = 0.55 // Give more space to preview
|
||||
|
||||
content := container.NewBorder(
|
||||
container.NewVBox(instructions, widget.NewSeparator(), fileLabel, container.NewHBox(loadBtn, clearBtn)),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
settingsPanel,
|
||||
mainContent,
|
||||
)
|
||||
|
||||
return container.NewBorder(topBar, nil, nil, nil, content)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user