Compare commits
3 Commits
b3e448f2fe
...
b41e41e5ad
| Author | SHA1 | Date | |
|---|---|---|---|
| b41e41e5ad | |||
| da49a1dd7b | |||
| 8cff33fcab |
|
|
@ -7,6 +7,8 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.leaktechnologies.dev/stu/VideoTools/internal/utils"
|
||||
)
|
||||
|
||||
// Result stores the outcome of a single encoder benchmark test
|
||||
|
|
@ -60,6 +62,7 @@ func (s *Suite) GenerateTestVideo(ctx context.Context, duration int) (string, er
|
|||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, s.FFmpegPath, args...)
|
||||
utils.ApplyNoWindow(cmd) // Hide command window on Windows during benchmark test video generation
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("failed to generate test video: %w", err)
|
||||
}
|
||||
|
|
@ -131,6 +134,7 @@ func (s *Suite) TestEncoder(ctx context.Context, encoder, preset string) Result
|
|||
// Measure encoding time
|
||||
start := time.Now()
|
||||
cmd := exec.CommandContext(ctx, s.FFmpegPath, args...)
|
||||
utils.ApplyNoWindow(cmd) // Hide command window on Windows during benchmark encoding test
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
result.Error = fmt.Sprintf("encoding failed: %v", err)
|
||||
|
|
|
|||
|
|
@ -212,7 +212,9 @@ func BuildQueueView(
|
|||
onViewLog func(string),
|
||||
onCopyCommand func(string),
|
||||
titleColor, bgColor, textColor color.Color,
|
||||
) (fyne.CanvasObject, *container.Scroll) {
|
||||
) (fyne.CanvasObject, *container.Scroll, []*StripedProgress) {
|
||||
// Track active progress animations to prevent goroutine leaks
|
||||
var activeProgress []*StripedProgress
|
||||
// Header
|
||||
title := canvas.NewText("JOB QUEUE", titleColor)
|
||||
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
|
||||
|
|
@ -264,7 +266,7 @@ func BuildQueueView(
|
|||
}
|
||||
|
||||
for _, job := range jobs {
|
||||
jobItems = append(jobItems, buildJobItem(job, queuePositions, onPause, onResume, onCancel, onRemove, onMoveUp, onMoveDown, onCopyError, onViewLog, onCopyCommand, bgColor, textColor))
|
||||
jobItems = append(jobItems, buildJobItem(job, queuePositions, onPause, onResume, onCancel, onRemove, onMoveUp, onMoveDown, onCopyError, onViewLog, onCopyCommand, bgColor, textColor, &activeProgress))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +282,7 @@ func BuildQueueView(
|
|||
scrollable,
|
||||
)
|
||||
|
||||
return container.NewPadded(body), scrollable
|
||||
return container.NewPadded(body), scrollable, activeProgress
|
||||
}
|
||||
|
||||
// buildJobItem creates a single job item in the queue list
|
||||
|
|
@ -297,6 +299,7 @@ func buildJobItem(
|
|||
onViewLog func(string),
|
||||
onCopyCommand func(string),
|
||||
bgColor, textColor color.Color,
|
||||
activeProgress *[]*StripedProgress,
|
||||
) fyne.CanvasObject {
|
||||
// Status color
|
||||
statusColor := GetStatusColor(job.Status)
|
||||
|
|
@ -325,6 +328,8 @@ func buildJobItem(
|
|||
if job.Status == queue.JobStatusRunning {
|
||||
progress.SetActivity(job.Progress <= 0.01)
|
||||
progress.StartAnimation()
|
||||
// Track active progress to stop animation on next refresh (prevents goroutine leaks)
|
||||
*activeProgress = append(*activeProgress, progress)
|
||||
} else {
|
||||
progress.SetActivity(false)
|
||||
progress.StopAnimation()
|
||||
|
|
|
|||
41
main.go
41
main.go
|
|
@ -986,6 +986,7 @@ type appState struct {
|
|||
|
||||
queueAutoRefreshStop chan struct{}
|
||||
queueAutoRefreshRunning bool
|
||||
queueActiveProgress []*ui.StripedProgress // Track active progress animations to prevent goroutine leaks
|
||||
|
||||
// Main menu refresh throttling
|
||||
mainMenuLastRefresh time.Time
|
||||
|
|
@ -1781,7 +1782,16 @@ func (s *appState) refreshQueueView() {
|
|||
}}, jobs...)
|
||||
}
|
||||
|
||||
view, scroll := ui.BuildQueueView(
|
||||
// CRITICAL: Stop all active progress animations before rebuilding to prevent goroutine leaks
|
||||
// Each refresh creates new StripedProgress widgets, and old animation goroutines must be stopped
|
||||
for _, progress := range s.queueActiveProgress {
|
||||
if progress != nil {
|
||||
progress.StopAnimation()
|
||||
}
|
||||
}
|
||||
s.queueActiveProgress = nil
|
||||
|
||||
view, scroll, activeProgress := ui.BuildQueueView(
|
||||
jobs,
|
||||
func() { // onBack
|
||||
// Stop auto-refresh before navigating away for snappy response
|
||||
|
|
@ -1938,6 +1948,9 @@ func (s *appState) refreshQueueView() {
|
|||
}()
|
||||
}
|
||||
|
||||
// Store active progress bars to stop them on next refresh
|
||||
s.queueActiveProgress = activeProgress
|
||||
|
||||
s.setContent(container.NewPadded(view))
|
||||
}
|
||||
|
||||
|
|
@ -1984,6 +1997,14 @@ func (s *appState) stopQueueAutoRefresh() {
|
|||
}
|
||||
s.queueAutoRefreshStop = nil
|
||||
s.queueAutoRefreshRunning = false
|
||||
|
||||
// Stop all active progress animations to prevent goroutine leaks when leaving queue view
|
||||
for _, progress := range s.queueActiveProgress {
|
||||
if progress != nil {
|
||||
progress.StopAnimation()
|
||||
}
|
||||
}
|
||||
s.queueActiveProgress = nil
|
||||
}
|
||||
|
||||
// addConvertToQueue adds a conversion job to the queue
|
||||
|
|
@ -6855,21 +6876,31 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
settingsContent.Hide()
|
||||
|
||||
settingsVisible := false
|
||||
toggleSettingsLabel := widget.NewLabel("Show Batch Settings")
|
||||
toggleSettingsLabel.Wrapping = fyne.TextWrapWord
|
||||
toggleSettingsLabel.Alignment = fyne.TextAlignCenter
|
||||
|
||||
var toggleSettingsBtn *widget.Button
|
||||
toggleSettingsBtn = widget.NewButton("Show Batch Settings", func() {
|
||||
toggleSettingsBtn = widget.NewButton("", func() {
|
||||
if settingsVisible {
|
||||
settingsContent.Hide()
|
||||
toggleSettingsBtn.SetText("Show Batch Settings")
|
||||
toggleSettingsLabel.SetText("Show Batch Settings")
|
||||
} else {
|
||||
settingsContent.Show()
|
||||
toggleSettingsBtn.SetText("Hide Batch Settings")
|
||||
toggleSettingsLabel.SetText("Hide Batch Settings")
|
||||
}
|
||||
settingsVisible = !settingsVisible
|
||||
})
|
||||
toggleSettingsBtn.Importance = widget.LowImportance
|
||||
|
||||
settingsBox := container.NewVBox(
|
||||
// Replace button text with wrapped label
|
||||
toggleSettingsBtnWithLabel := container.NewStack(
|
||||
toggleSettingsBtn,
|
||||
container.NewPadded(toggleSettingsLabel),
|
||||
)
|
||||
|
||||
settingsBox := container.NewVBox(
|
||||
toggleSettingsBtnWithLabel,
|
||||
settingsContent,
|
||||
widget.NewSeparator(),
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user