Wire convert button with status indicator

This commit is contained in:
Stu 2025-11-21 16:22:50 -05:00
parent f7a7246301
commit 8420afb000

125
main.go
View File

@ -140,6 +140,8 @@ type appState struct {
playerLast time.Time playerLast time.Time
progressQuit chan struct{} progressQuit chan struct{}
playerSurf *playerSurface playerSurf *playerSurface
convertBusy bool
convertStatus string
playSess *playSession playSess *playSession
} }
@ -682,12 +684,27 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
aspectOptions.SetSelected("Auto") aspectOptions.SetSelected("Auto")
debugLog(logCatUI, "convert settings reset to defaults") debugLog(logCatUI, "convert settings reset to defaults")
}) })
convertBtn := widget.NewButton("CONVERT", func() { statusLabel := widget.NewLabel("")
debugLog(logCatModule, "convert action triggered -> %s", state.convert.OutputFile()) if state.convertBusy {
statusLabel.SetText(state.convertStatus)
} else if src != nil {
statusLabel.SetText("Ready to convert")
} else {
statusLabel.SetText("Load a video to convert")
}
var convertBtn *widget.Button
convertBtn = widget.NewButton("CONVERT", func() {
state.startConvert(statusLabel, convertBtn)
}) })
convertBtn.Importance = widget.HighImportance convertBtn.Importance = widget.HighImportance
if src == nil {
convertBtn.Disable()
}
if state.convertBusy {
convertBtn.Disable()
}
actionInner := container.NewHBox(resetBtn, layout.NewSpacer(), convertBtn) actionInner := container.NewHBox(resetBtn, statusLabel, layout.NewSpacer(), convertBtn)
actionBar := tintedBar(convertColor, actionInner) actionBar := tintedBar(convertColor, actionInner)
return container.NewBorder( return container.NewBorder(
@ -1654,6 +1671,8 @@ func (s *appState) clearVideo() {
s.stopPlayer() s.stopPlayer()
s.source = nil s.source = nil
s.currentFrame = "" s.currentFrame = ""
s.convertBusy = false
s.convertStatus = ""
s.convert.OutputBase = "converted" s.convert.OutputBase = "converted"
s.convert.CoverArtPath = "" s.convert.CoverArtPath = ""
s.convert.AspectHandling = "Auto" s.convert.AspectHandling = "Auto"
@ -1661,6 +1680,106 @@ func (s *appState) clearVideo() {
s.showConvertView(nil) s.showConvertView(nil)
}, false) }, false)
} }
func crfForQuality(q string) string {
switch q {
case "Draft (CRF 28)":
return "28"
case "High (CRF 18)":
return "18"
case "Lossless":
return "0"
default:
return "23"
}
}
func (s *appState) startConvert(status *widget.Label, btn *widget.Button) {
if s.source == nil {
dialog.ShowInformation("Convert", "Load a video first.", s.window)
return
}
if s.convertBusy {
return
}
src := s.source
cfg := s.convert
outDir := filepath.Dir(src.Path)
outName := cfg.OutputFile()
if outName == "" {
outName = "converted" + cfg.SelectedFormat.Ext
}
outPath := filepath.Join(outDir, outName)
if outPath == src.Path {
outPath = filepath.Join(outDir, "converted-"+outName)
}
args := []string{
"-y",
"-hide_banner",
"-loglevel", "error",
"-i", src.Path,
}
// Video filters.
var vf []string
if cfg.InverseTelecine {
vf = append(vf, "yadif")
}
if len(vf) > 0 {
args = append(args, "-vf", strings.Join(vf, ","))
}
// Video codec and quality.
args = append(args, "-c:v", cfg.SelectedFormat.VideoCodec)
crf := crfForQuality(cfg.Quality)
if cfg.SelectedFormat.VideoCodec == "libx264" || cfg.SelectedFormat.VideoCodec == "libx265" {
args = append(args, "-crf", crf, "-preset", "medium")
}
// Audio: copy if present.
args = append(args, "-c:a", "copy")
args = append(args, outPath)
debugLog(logCatFFMPEG, "convert command: ffmpeg %s", strings.Join(args, " "))
s.convertBusy = true
s.convertStatus = "Converting..."
if status != nil {
status.SetText(s.convertStatus)
}
if btn != nil {
btn.Disable()
}
go func() {
cmd := exec.Command("ffmpeg", args...)
out, err := cmd.CombinedOutput()
if err != nil {
debugLog(logCatFFMPEG, "convert failed: %v output=%s", err, strings.TrimSpace(string(out)))
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
dialog.ShowError(fmt.Errorf("convert failed: %w", err), s.window)
s.convertBusy = false
s.convertStatus = "Failed"
if status != nil {
status.SetText(s.convertStatus)
}
if btn != nil {
btn.Enable()
}
}, false)
return
}
debugLog(logCatFFMPEG, "convert completed: %s", outPath)
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
dialog.ShowInformation("Convert", fmt.Sprintf("Saved %s", outPath), s.window)
s.convertBusy = false
s.convertStatus = "Done"
if status != nil {
status.SetText(s.convertStatus)
}
if btn != nil {
btn.Enable()
}
}, false)
}()
}
func (s *appState) generateSnippet() { func (s *appState) generateSnippet() {
if s.source == nil { if s.source == nil {
return return