Improve benchmark results sorting and cancel flow
This commit is contained in:
parent
73e527048a
commit
5b76da0fdf
55
DONE.md
55
DONE.md
|
|
@ -2,9 +2,59 @@
|
||||||
|
|
||||||
This file tracks completed features, fixes, and milestones.
|
This file tracks completed features, fixes, and milestones.
|
||||||
|
|
||||||
## Version 0.1.0-dev19 (2025-12-18) - Convert Module Cleanup & UX Polish
|
## Version 0.1.0-dev19 (2025-12-18 to 2025-12-20) - Convert Module Cleanup & UX Polish
|
||||||
|
|
||||||
### Features
|
### Features (2025-12-20 Session)
|
||||||
|
- ✅ **History Sidebar - In Progress Tab**
|
||||||
|
- Added "In Progress" tab to history sidebar
|
||||||
|
- Shows running and pending jobs without opening queue
|
||||||
|
- Animated striped progress bars per module color
|
||||||
|
- Real-time progress updates (0-100%)
|
||||||
|
- No delete button on active jobs (only completed/failed)
|
||||||
|
- Dynamic status text ("Running..." or "Pending")
|
||||||
|
|
||||||
|
- ✅ **Benchmark System Overhaul**
|
||||||
|
- **Hardware Detection Module** (`internal/sysinfo/sysinfo.go`)
|
||||||
|
- Cross-platform CPU detection (model, cores, clock speed)
|
||||||
|
- GPU detection with driver version (NVIDIA via nvidia-smi)
|
||||||
|
- RAM detection with human-readable formatting
|
||||||
|
- Linux, Windows, macOS support
|
||||||
|
- **Hardware Info Display**
|
||||||
|
- Shown immediately in benchmark progress view (before tests run)
|
||||||
|
- Displayed in benchmark results view
|
||||||
|
- Saved with each benchmark run for history
|
||||||
|
- **Settings Persistence**
|
||||||
|
- Hardware acceleration settings saved with benchmarks
|
||||||
|
- Settings persist between sessions via config file
|
||||||
|
- GPU automatically detected and used
|
||||||
|
- **UI Polish**
|
||||||
|
- "Run Benchmark" button highlighted (HighImportance) on first run
|
||||||
|
- Returns to normal styling after initial benchmark
|
||||||
|
- Guides new users to run initial benchmark
|
||||||
|
|
||||||
|
- ✅ **Bitrate Preset Simplification**
|
||||||
|
- Reduced from 13 confusing options to 6 clear presets
|
||||||
|
- Removed resolution references (no more "1440p" confusion)
|
||||||
|
- Codec-agnostic (presets don't change selected codec)
|
||||||
|
- Quality-based naming: Low/Medium/Good/High/Very High Quality
|
||||||
|
- Focused on common use cases (1.5-8 Mbps range)
|
||||||
|
- Presets only set bitrate and switch to CBR mode
|
||||||
|
- User codec choice (H.264, VP9, AV1, etc.) preserved
|
||||||
|
|
||||||
|
- ✅ **Quality Preset Codec Compatibility**
|
||||||
|
- "Lossless" quality option only available for H.265 and AV1
|
||||||
|
- Dynamic quality dropdown based on selected codec
|
||||||
|
- Automatic fallback to "Near-Lossless" when switching to non-lossless codec
|
||||||
|
- Lossless + Target Size bitrate mode now supported for H.265/AV1
|
||||||
|
- Prevents invalid codec/quality combinations
|
||||||
|
|
||||||
|
- ✅ **App Icon Improvements**
|
||||||
|
- Regenerated VT_Icon.ico with transparent background
|
||||||
|
- Updated LoadAppIcon() to search PNG first (better Linux support)
|
||||||
|
- Searches both current directory and executable directory
|
||||||
|
- Added debug logging for icon loading troubleshooting
|
||||||
|
|
||||||
|
### Features (2025-12-18 Session)
|
||||||
- ✅ **History Sidebar Enhancements**
|
- ✅ **History Sidebar Enhancements**
|
||||||
- Delete button ("×") on each history entry
|
- Delete button ("×") on each history entry
|
||||||
- Remove individual entries from history
|
- Remove individual entries from history
|
||||||
|
|
@ -715,6 +765,7 @@ This file tracks completed features, fixes, and milestones.
|
||||||
|
|
||||||
### Recent Fixes
|
### Recent Fixes
|
||||||
- ✅ Fixed aspect ratio default from 16:9 to Source (dev7)
|
- ✅ Fixed aspect ratio default from 16:9 to Source (dev7)
|
||||||
|
- ✅ Ranked benchmark results by score and added cancel confirmation
|
||||||
- ✅ Stabilized video seeking and embedded rendering
|
- ✅ Stabilized video seeking and embedded rendering
|
||||||
- ✅ Improved player window positioning
|
- ✅ Improved player window positioning
|
||||||
- ✅ Fixed clear video functionality
|
- ✅ Fixed clear video functionality
|
||||||
|
|
|
||||||
29
TODO.md
29
TODO.md
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
This file tracks upcoming features, improvements, and known issues.
|
This file tracks upcoming features, improvements, and known issues.
|
||||||
|
|
||||||
## Current Focus: dev19 - Convert Module Cleanup & Polish
|
## Current Focus: dev20+ - Feature Development
|
||||||
|
|
||||||
### In Progress
|
### In Progress
|
||||||
- [ ] **AI Frame Interpolation Support**
|
- [ ] **AI Frame Interpolation Support** (Deferred to dev20+)
|
||||||
- RIFE (Real-Time Intermediate Flow Estimation) - https://github.com/hzwer/ECCV2022-RIFE
|
- RIFE (Real-Time Intermediate Flow Estimation) - https://github.com/hzwer/ECCV2022-RIFE
|
||||||
- FILM (Frame Interpolation for Large Motion) - https://github.com/google-research/frame-interpolation
|
- FILM (Frame Interpolation for Large Motion) - https://github.com/google-research/frame-interpolation
|
||||||
- DAIN (Depth-Aware Video Frame Interpolation) - https://github.com/baowenbo/DAIN
|
- DAIN (Depth-Aware Video Frame Interpolation) - https://github.com/baowenbo/DAIN
|
||||||
|
|
@ -14,11 +14,34 @@ This file tracks upcoming features, improvements, and known issues.
|
||||||
- Model download/management system
|
- Model download/management system
|
||||||
- UI controls for model selection
|
- UI controls for model selection
|
||||||
|
|
||||||
- [ ] **Color Space Preservation**
|
- [ ] **Color Space Preservation** (Deferred to dev20+)
|
||||||
- Fix color space preservation in upscale module
|
- Fix color space preservation in upscale module
|
||||||
- Ensure all conversions preserve color metadata (color_space, color_primaries, color_trc, color_range)
|
- Ensure all conversions preserve color metadata (color_space, color_primaries, color_trc, color_range)
|
||||||
- Test with HDR content
|
- Test with HDR content
|
||||||
|
|
||||||
|
### Completed in dev19 (2025-12-20)
|
||||||
|
- [x] **History Sidebar - In Progress Tab** ✅ COMPLETED
|
||||||
|
- Shows running/pending jobs without opening full queue
|
||||||
|
- Animated progress bars per module color
|
||||||
|
- Real-time progress updates
|
||||||
|
|
||||||
|
- [x] **Benchmark System Overhaul** ✅ COMPLETED
|
||||||
|
- Hardware detection module (CPU, GPU, RAM, drivers)
|
||||||
|
- Hardware info displayed in progress and results views
|
||||||
|
- Settings persistence across sessions
|
||||||
|
- First-run button highlighting
|
||||||
|
- Results ranked by score with cancel confirmation
|
||||||
|
|
||||||
|
- [x] **Bitrate Preset Simplification** ✅ COMPLETED
|
||||||
|
- Codec-agnostic quality-based presets
|
||||||
|
- Removed confusing resolution references
|
||||||
|
- 6 clear presets: Manual, Low, Medium, Good, High, Very High
|
||||||
|
|
||||||
|
- [x] **Quality Preset Codec Compatibility** ✅ COMPLETED
|
||||||
|
- Lossless option only for H.265/AV1
|
||||||
|
- Dynamic dropdown based on codec
|
||||||
|
- Lossless + Target Size mode support
|
||||||
|
|
||||||
## Priority Features for dev20+
|
## Priority Features for dev20+
|
||||||
|
|
||||||
### Quality & Polish Improvements
|
### Quality & Polish Improvements
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,24 @@ package ui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/benchmark"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/benchmark"
|
||||||
|
"git.leaktechnologies.dev/stu/VideoTools/internal/sysinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildBenchmarkProgressView creates the benchmark progress UI
|
// BuildBenchmarkProgressView creates the benchmark progress UI
|
||||||
func BuildBenchmarkProgressView(
|
func BuildBenchmarkProgressView(
|
||||||
|
hwInfo sysinfo.HardwareInfo,
|
||||||
onCancel func(),
|
onCancel func(),
|
||||||
titleColor, bgColor, textColor color.Color,
|
titleColor, bgColor, textColor color.Color,
|
||||||
) *BenchmarkProgressView {
|
) *BenchmarkProgressView {
|
||||||
view := &BenchmarkProgressView{
|
view := &BenchmarkProgressView{
|
||||||
|
hwInfo: hwInfo,
|
||||||
titleColor: titleColor,
|
titleColor: titleColor,
|
||||||
bgColor: bgColor,
|
bgColor: bgColor,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
|
|
@ -28,6 +32,7 @@ func BuildBenchmarkProgressView(
|
||||||
|
|
||||||
// BenchmarkProgressView shows real-time benchmark progress
|
// BenchmarkProgressView shows real-time benchmark progress
|
||||||
type BenchmarkProgressView struct {
|
type BenchmarkProgressView struct {
|
||||||
|
hwInfo sysinfo.HardwareInfo
|
||||||
titleColor color.Color
|
titleColor color.Color
|
||||||
bgColor color.Color
|
bgColor color.Color
|
||||||
textColor color.Color
|
textColor color.Color
|
||||||
|
|
@ -57,6 +62,37 @@ func (v *BenchmarkProgressView) build() {
|
||||||
container.NewCenter(title),
|
container.NewCenter(title),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Hardware info section
|
||||||
|
hwInfoTitle := widget.NewLabel("System Hardware")
|
||||||
|
hwInfoTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
hwInfoTitle.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
|
cpuLabel := widget.NewLabel(fmt.Sprintf("CPU: %s (%d cores @ %s)", v.hwInfo.CPU, v.hwInfo.CPUCores, v.hwInfo.CPUMHz))
|
||||||
|
cpuLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
gpuLabel := widget.NewLabel(fmt.Sprintf("GPU: %s", v.hwInfo.GPU))
|
||||||
|
gpuLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
ramLabel := widget.NewLabel(fmt.Sprintf("RAM: %s", v.hwInfo.RAM))
|
||||||
|
|
||||||
|
driverLabel := widget.NewLabel(fmt.Sprintf("Driver: %s", v.hwInfo.GPUDriver))
|
||||||
|
driverLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
hwCard := canvas.NewRectangle(color.RGBA{R: 34, G: 38, B: 48, A: 255})
|
||||||
|
hwCard.CornerRadius = 8
|
||||||
|
|
||||||
|
hwContent := container.NewVBox(
|
||||||
|
hwInfoTitle,
|
||||||
|
cpuLabel,
|
||||||
|
gpuLabel,
|
||||||
|
ramLabel,
|
||||||
|
driverLabel,
|
||||||
|
)
|
||||||
|
|
||||||
|
hwInfoSection := container.NewPadded(
|
||||||
|
container.NewMax(hwCard, hwContent),
|
||||||
|
)
|
||||||
|
|
||||||
// Status section
|
// Status section
|
||||||
v.statusLabel = widget.NewLabel("Initializing benchmark...")
|
v.statusLabel = widget.NewLabel("Initializing benchmark...")
|
||||||
v.statusLabel.TextStyle = fyne.TextStyle{Bold: true}
|
v.statusLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
|
@ -96,6 +132,8 @@ func (v *BenchmarkProgressView) build() {
|
||||||
header,
|
header,
|
||||||
nil, nil, nil,
|
nil, nil, nil,
|
||||||
container.NewVBox(
|
container.NewVBox(
|
||||||
|
hwInfoSection,
|
||||||
|
widget.NewSeparator(),
|
||||||
statusSection,
|
statusSection,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
resultsSection,
|
resultsSection,
|
||||||
|
|
@ -188,6 +226,7 @@ func (v *BenchmarkProgressView) SetComplete() {
|
||||||
func BuildBenchmarkResultsView(
|
func BuildBenchmarkResultsView(
|
||||||
results []benchmark.Result,
|
results []benchmark.Result,
|
||||||
recommendation benchmark.Result,
|
recommendation benchmark.Result,
|
||||||
|
hwInfo sysinfo.HardwareInfo,
|
||||||
onApply func(),
|
onApply func(),
|
||||||
onClose func(),
|
onClose func(),
|
||||||
titleColor, bgColor, textColor color.Color,
|
titleColor, bgColor, textColor color.Color,
|
||||||
|
|
@ -207,6 +246,37 @@ func BuildBenchmarkResultsView(
|
||||||
container.NewCenter(title),
|
container.NewCenter(title),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Hardware info section
|
||||||
|
hwInfoTitle := widget.NewLabel("System Hardware")
|
||||||
|
hwInfoTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
hwInfoTitle.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
|
cpuLabel := widget.NewLabel(fmt.Sprintf("CPU: %s (%d cores @ %s)", hwInfo.CPU, hwInfo.CPUCores, hwInfo.CPUMHz))
|
||||||
|
cpuLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
gpuLabel := widget.NewLabel(fmt.Sprintf("GPU: %s", hwInfo.GPU))
|
||||||
|
gpuLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
ramLabel := widget.NewLabel(fmt.Sprintf("RAM: %s", hwInfo.RAM))
|
||||||
|
|
||||||
|
driverLabel := widget.NewLabel(fmt.Sprintf("Driver: %s", hwInfo.GPUDriver))
|
||||||
|
driverLabel.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
hwCard := canvas.NewRectangle(color.RGBA{R: 34, G: 38, B: 48, A: 255})
|
||||||
|
hwCard.CornerRadius = 8
|
||||||
|
|
||||||
|
hwContent := container.NewVBox(
|
||||||
|
hwInfoTitle,
|
||||||
|
cpuLabel,
|
||||||
|
gpuLabel,
|
||||||
|
ramLabel,
|
||||||
|
driverLabel,
|
||||||
|
)
|
||||||
|
|
||||||
|
hwInfoSection := container.NewPadded(
|
||||||
|
container.NewMax(hwCard, hwContent),
|
||||||
|
)
|
||||||
|
|
||||||
// Recommendation section
|
// Recommendation section
|
||||||
if recommendation.Encoder != "" {
|
if recommendation.Encoder != "" {
|
||||||
recTitle := widget.NewLabel("RECOMMENDED ENCODER")
|
recTitle := widget.NewLabel("RECOMMENDED ENCODER")
|
||||||
|
|
@ -243,12 +313,19 @@ func BuildBenchmarkResultsView(
|
||||||
topResultsTitle.TextStyle = fyne.TextStyle{Bold: true}
|
topResultsTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
topResultsTitle.Alignment = fyne.TextAlignCenter
|
topResultsTitle.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
var resultItems []fyne.CanvasObject
|
var filtered []benchmark.Result
|
||||||
for i, result := range results {
|
for _, result := range results {
|
||||||
if result.Error != "" {
|
if result.Error == "" {
|
||||||
continue
|
filtered = append(filtered, result)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(filtered, func(i, j int) bool {
|
||||||
|
return filtered[i].Score > filtered[j].Score
|
||||||
|
})
|
||||||
|
|
||||||
|
var resultItems []fyne.CanvasObject
|
||||||
|
for i, result := range filtered {
|
||||||
rankLabel := widget.NewLabel(fmt.Sprintf("#%d", i+1))
|
rankLabel := widget.NewLabel(fmt.Sprintf("#%d", i+1))
|
||||||
rankLabel.TextStyle = fyne.TextStyle{Bold: true}
|
rankLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
|
||||||
|
|
@ -290,6 +367,8 @@ func BuildBenchmarkResultsView(
|
||||||
header,
|
header,
|
||||||
nil, nil, nil,
|
nil, nil, nil,
|
||||||
container.NewVBox(
|
container.NewVBox(
|
||||||
|
hwInfoSection,
|
||||||
|
widget.NewSeparator(),
|
||||||
recommendationSection,
|
recommendationSection,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
resultsSection,
|
resultsSection,
|
||||||
|
|
|
||||||
110
main.go
110
main.go
|
|
@ -44,6 +44,7 @@ import (
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/modules"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/modules"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/player"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/player"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/queue"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/queue"
|
||||||
|
"git.leaktechnologies.dev/stu/VideoTools/internal/sysinfo"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/thumbnail"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/thumbnail"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/ui"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/ui"
|
||||||
"git.leaktechnologies.dev/stu/VideoTools/internal/utils"
|
"git.leaktechnologies.dev/stu/VideoTools/internal/utils"
|
||||||
|
|
@ -81,16 +82,17 @@ var (
|
||||||
nvencRuntimeOK bool
|
nvencRuntimeOK bool
|
||||||
|
|
||||||
modulesList = []Module{
|
modulesList = []Module{
|
||||||
{"convert", "Convert", utils.MustHex("#8B44FF"), "Convert", modules.HandleConvert}, // Violet
|
{"convert", "Convert", utils.MustHex("#8B44FF"), "Convert", modules.HandleConvert}, // Violet
|
||||||
{"merge", "Merge", utils.MustHex("#4488FF"), "Convert", modules.HandleMerge}, // Blue
|
{"merge", "Merge", utils.MustHex("#4488FF"), "Convert", modules.HandleMerge}, // Blue
|
||||||
{"trim", "Trim", utils.MustHex("#44DDFF"), "Convert", modules.HandleTrim}, // Cyan
|
{"trim", "Trim", utils.MustHex("#44DDFF"), "Convert", modules.HandleTrim}, // Cyan
|
||||||
{"filters", "Filters", utils.MustHex("#44FF88"), "Convert", modules.HandleFilters}, // Green
|
{"filters", "Filters", utils.MustHex("#44FF88"), "Convert", modules.HandleFilters}, // Green
|
||||||
{"upscale", "Upscale", utils.MustHex("#AAFF44"), "Advanced", modules.HandleUpscale}, // Yellow-Green
|
{"upscale", "Upscale", utils.MustHex("#AAFF44"), "Advanced", modules.HandleUpscale}, // Yellow-Green
|
||||||
{"audio", "Audio", utils.MustHex("#FFD744"), "Convert", modules.HandleAudio}, // Yellow
|
{"audio", "Audio", utils.MustHex("#FFD744"), "Convert", modules.HandleAudio}, // Yellow
|
||||||
{"thumb", "Thumb", utils.MustHex("#FF8844"), "Screenshots", modules.HandleThumb}, // Orange
|
{"subtitles", "Subtitles", utils.MustHex("#44A6FF"), "Convert", modules.HandleSubtitles}, // Azure
|
||||||
{"compare", "Compare", utils.MustHex("#FF44AA"), "Inspect", modules.HandleCompare}, // Pink
|
{"thumb", "Thumb", utils.MustHex("#FF8844"), "Screenshots", modules.HandleThumb}, // Orange
|
||||||
{"inspect", "Inspect", utils.MustHex("#FF4444"), "Inspect", modules.HandleInspect}, // Red
|
{"compare", "Compare", utils.MustHex("#FF44AA"), "Inspect", modules.HandleCompare}, // Pink
|
||||||
{"player", "Player", utils.MustHex("#44FFDD"), "Playback", modules.HandlePlayer}, // Teal
|
{"inspect", "Inspect", utils.MustHex("#FF4444"), "Inspect", modules.HandleInspect}, // Red
|
||||||
|
{"player", "Player", utils.MustHex("#44FFDD"), "Playback", modules.HandlePlayer}, // Teal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform-specific configuration
|
// Platform-specific configuration
|
||||||
|
|
@ -557,12 +559,13 @@ func savePersistedConvertConfig(cfg convertConfig) error {
|
||||||
|
|
||||||
// benchmarkRun represents a single benchmark test run
|
// benchmarkRun represents a single benchmark test run
|
||||||
type benchmarkRun struct {
|
type benchmarkRun struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
Results []benchmark.Result `json:"results"`
|
Results []benchmark.Result `json:"results"`
|
||||||
RecommendedEncoder string `json:"recommended_encoder"`
|
RecommendedEncoder string `json:"recommended_encoder"`
|
||||||
RecommendedPreset string `json:"recommended_preset"`
|
RecommendedPreset string `json:"recommended_preset"`
|
||||||
RecommendedHWAccel string `json:"recommended_hwaccel"`
|
RecommendedHWAccel string `json:"recommended_hwaccel"`
|
||||||
RecommendedFPS float64 `json:"recommended_fps"`
|
RecommendedFPS float64 `json:"recommended_fps"`
|
||||||
|
HardwareInfo sysinfo.HardwareInfo `json:"hardware_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// benchmarkConfig holds benchmark history
|
// benchmarkConfig holds benchmark history
|
||||||
|
|
@ -1355,7 +1358,7 @@ func (s *appState) showMainMenu() {
|
||||||
Label: m.Label,
|
Label: m.Label,
|
||||||
Color: m.Color,
|
Color: m.Color,
|
||||||
Category: m.Category,
|
Category: m.Category,
|
||||||
Enabled: m.ID == "convert" || m.ID == "compare" || m.ID == "inspect" || m.ID == "merge" || m.ID == "thumb" || m.ID == "player" || m.ID == "filters" || m.ID == "upscale", // Enabled modules
|
Enabled: m.ID == "convert" || m.ID == "compare" || m.ID == "inspect" || m.ID == "merge" || m.ID == "thumb" || m.ID == "player" || m.ID == "filters" || m.ID == "upscale", // Enabled modules (subtitles placeholder stays disabled)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1408,11 +1411,17 @@ func (s *appState) showMainMenu() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if benchmark has been run
|
||||||
|
hasBenchmark := false
|
||||||
|
if cfg, err := loadBenchmarkConfig(); err == nil && len(cfg.History) > 0 {
|
||||||
|
hasBenchmark = true
|
||||||
|
}
|
||||||
|
|
||||||
menu := ui.BuildMainMenu(mods, s.showModule, s.handleModuleDrop, s.showQueue, nil, s.showBenchmark, s.showBenchmarkHistory, func() {
|
menu := ui.BuildMainMenu(mods, s.showModule, s.handleModuleDrop, s.showQueue, nil, s.showBenchmark, s.showBenchmarkHistory, func() {
|
||||||
// Toggle sidebar
|
// Toggle sidebar
|
||||||
s.sidebarVisible = !s.sidebarVisible
|
s.sidebarVisible = !s.sidebarVisible
|
||||||
s.showMainMenu()
|
s.showMainMenu()
|
||||||
}, s.sidebarVisible, sidebar, titleColor, queueColor, textColor, queueCompleted, queueTotal)
|
}, s.sidebarVisible, sidebar, titleColor, queueColor, textColor, queueCompleted, queueTotal, hasBenchmark)
|
||||||
|
|
||||||
// Update stats bar
|
// Update stats bar
|
||||||
s.updateStatsBar()
|
s.updateStatsBar()
|
||||||
|
|
@ -1733,17 +1742,35 @@ func (s *appState) showBenchmark() {
|
||||||
s.stopPlayer()
|
s.stopPlayer()
|
||||||
s.active = "benchmark"
|
s.active = "benchmark"
|
||||||
|
|
||||||
|
// Detect hardware info upfront
|
||||||
|
hwInfo := sysinfo.Detect()
|
||||||
|
logging.Debug(logging.CatSystem, "detected hardware for benchmark: %s", hwInfo.Summary())
|
||||||
|
|
||||||
// Create benchmark suite
|
// Create benchmark suite
|
||||||
tmpDir := filepath.Join(os.TempDir(), "videotools-benchmark")
|
tmpDir := filepath.Join(os.TempDir(), "videotools-benchmark")
|
||||||
_ = os.MkdirAll(tmpDir, 0o755)
|
_ = os.MkdirAll(tmpDir, 0o755)
|
||||||
|
|
||||||
suite := benchmark.NewSuite(platformConfig.FFmpegPath, tmpDir)
|
suite := benchmark.NewSuite(platformConfig.FFmpegPath, tmpDir)
|
||||||
|
|
||||||
// Build progress view
|
benchComplete := atomic.Bool{}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Build progress view with hardware info
|
||||||
view := ui.BuildBenchmarkProgressView(
|
view := ui.BuildBenchmarkProgressView(
|
||||||
|
hwInfo,
|
||||||
func() {
|
func() {
|
||||||
// Cancel benchmark
|
if benchComplete.Load() {
|
||||||
s.showMainMenu()
|
s.showMainMenu()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.ShowConfirm("Cancel Benchmark?", "The benchmark is still running. Cancel it now?", func(ok bool) {
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
s.showMainMenu()
|
||||||
|
}, s.window)
|
||||||
},
|
},
|
||||||
utils.MustHex("#4CE870"),
|
utils.MustHex("#4CE870"),
|
||||||
utils.MustHex("#1E1E1E"),
|
utils.MustHex("#1E1E1E"),
|
||||||
|
|
@ -1754,12 +1781,13 @@ func (s *appState) showBenchmark() {
|
||||||
|
|
||||||
// Run benchmark in background
|
// Run benchmark in background
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Generate test video
|
// Generate test video
|
||||||
view.UpdateProgress(0, 100, "Generating test video", "")
|
view.UpdateProgress(0, 100, "Generating test video", "")
|
||||||
testPath, err := suite.GenerateTestVideo(ctx, 30)
|
testPath, err := suite.GenerateTestVideo(ctx, 30)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
logging.Debug(logging.CatSystem, "failed to generate test video: %v", err)
|
logging.Debug(logging.CatSystem, "failed to generate test video: %v", err)
|
||||||
dialog.ShowError(fmt.Errorf("failed to generate test video: %w", err), s.window)
|
dialog.ShowError(fmt.Errorf("failed to generate test video: %w", err), s.window)
|
||||||
s.showMainMenu()
|
s.showMainMenu()
|
||||||
|
|
@ -1780,6 +1808,9 @@ func (s *appState) showBenchmark() {
|
||||||
// Run benchmark suite
|
// Run benchmark suite
|
||||||
err = suite.RunFullSuite(ctx, availableEncoders)
|
err = suite.RunFullSuite(ctx, availableEncoders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
logging.Debug(logging.CatSystem, "benchmark failed: %v", err)
|
logging.Debug(logging.CatSystem, "benchmark failed: %v", err)
|
||||||
dialog.ShowError(fmt.Errorf("benchmark failed: %w", err), s.window)
|
dialog.ShowError(fmt.Errorf("benchmark failed: %w", err), s.window)
|
||||||
s.showMainMenu()
|
s.showMainMenu()
|
||||||
|
|
@ -1793,6 +1824,7 @@ func (s *appState) showBenchmark() {
|
||||||
|
|
||||||
// Mark complete
|
// Mark complete
|
||||||
view.SetComplete()
|
view.SetComplete()
|
||||||
|
benchComplete.Store(true)
|
||||||
|
|
||||||
// Get recommendation
|
// Get recommendation
|
||||||
encoder, preset, rec := suite.GetRecommendation()
|
encoder, preset, rec := suite.GetRecommendation()
|
||||||
|
|
@ -1807,10 +1839,13 @@ func (s *appState) showBenchmark() {
|
||||||
|
|
||||||
// Show results dialog with option to apply
|
// Show results dialog with option to apply
|
||||||
go func() {
|
go func() {
|
||||||
|
// Detect hardware info for display
|
||||||
|
hwInfo := sysinfo.Detect()
|
||||||
allResults := suite.Results // Show all results, not just top 10
|
allResults := suite.Results // Show all results, not just top 10
|
||||||
resultsView := ui.BuildBenchmarkResultsView(
|
resultsView := ui.BuildBenchmarkResultsView(
|
||||||
allResults,
|
allResults,
|
||||||
rec,
|
rec,
|
||||||
|
hwInfo,
|
||||||
func() {
|
func() {
|
||||||
// Apply recommended settings
|
// Apply recommended settings
|
||||||
s.applyBenchmarkRecommendation(encoder, preset)
|
s.applyBenchmarkRecommendation(encoder, preset)
|
||||||
|
|
@ -1876,6 +1911,10 @@ func (s *appState) saveBenchmarkRun(results []benchmark.Result, encoder, preset
|
||||||
hwAccel = "none"
|
hwAccel = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect hardware info
|
||||||
|
hwInfo := sysinfo.Detect()
|
||||||
|
logging.Debug(logging.CatSystem, "detected hardware: %s", hwInfo.Summary())
|
||||||
|
|
||||||
// Load existing config
|
// Load existing config
|
||||||
cfg, err := loadBenchmarkConfig()
|
cfg, err := loadBenchmarkConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1891,6 +1930,7 @@ func (s *appState) saveBenchmarkRun(results []benchmark.Result, encoder, preset
|
||||||
RecommendedPreset: preset,
|
RecommendedPreset: preset,
|
||||||
RecommendedHWAccel: hwAccel,
|
RecommendedHWAccel: hwAccel,
|
||||||
RecommendedFPS: fps,
|
RecommendedFPS: fps,
|
||||||
|
HardwareInfo: hwInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to history (keep last 10 runs)
|
// Add to history (keep last 10 runs)
|
||||||
|
|
@ -1991,6 +2031,7 @@ func (s *appState) showBenchmarkHistory() {
|
||||||
resultsView := ui.BuildBenchmarkResultsView(
|
resultsView := ui.BuildBenchmarkResultsView(
|
||||||
run.Results,
|
run.Results,
|
||||||
rec,
|
rec,
|
||||||
|
run.HardwareInfo,
|
||||||
func() {
|
func() {
|
||||||
// Apply this recommendation
|
// Apply this recommendation
|
||||||
s.applyBenchmarkRecommendation(run.RecommendedEncoder, run.RecommendedPreset)
|
s.applyBenchmarkRecommendation(run.RecommendedEncoder, run.RecommendedPreset)
|
||||||
|
|
@ -5786,9 +5827,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
"Target Size (Calculate from file size)",
|
"Target Size (Calculate from file size)",
|
||||||
}
|
}
|
||||||
bitrateModeMap := map[string]string{
|
bitrateModeMap := map[string]string{
|
||||||
"CRF (Constant Rate Factor)": "CRF",
|
"CRF (Constant Rate Factor)": "CRF",
|
||||||
"CBR (Constant Bitrate)": "CBR",
|
"CBR (Constant Bitrate)": "CBR",
|
||||||
"VBR (Variable Bitrate)": "VBR",
|
"VBR (Variable Bitrate)": "VBR",
|
||||||
"Target Size (Calculate from file size)": "Target Size",
|
"Target Size (Calculate from file size)": "Target Size",
|
||||||
}
|
}
|
||||||
reverseMap := map[string]string{
|
reverseMap := map[string]string{
|
||||||
|
|
@ -5857,18 +5898,11 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
|
|
||||||
presets := []bitratePreset{
|
presets := []bitratePreset{
|
||||||
{Label: "Manual", Bitrate: "", Codec: ""},
|
{Label: "Manual", Bitrate: "", Codec: ""},
|
||||||
{Label: "AV1 1080p - 1200k (smallest)", Bitrate: "1200k", Codec: "AV1"},
|
{Label: "1.5 Mbps - Low Quality", Bitrate: "1500k", Codec: ""},
|
||||||
{Label: "AV1 1080p - 1400k (sweet spot)", Bitrate: "1400k", Codec: "AV1"},
|
{Label: "2.5 Mbps - Medium Quality", Bitrate: "2500k", Codec: ""},
|
||||||
{Label: "AV1 1080p - 1800k (headroom)", Bitrate: "1800k", Codec: "AV1"},
|
{Label: "4.0 Mbps - Good Quality", Bitrate: "4000k", Codec: ""},
|
||||||
{Label: "H.265 1080p - 2000k (balanced)", Bitrate: "2000k", Codec: "H.265"},
|
{Label: "6.0 Mbps - High Quality", Bitrate: "6000k", Codec: ""},
|
||||||
{Label: "H.265 1080p - 2400k (noisy sources)", Bitrate: "2400k", Codec: "H.265"},
|
{Label: "8.0 Mbps - Very High Quality", Bitrate: "8000k", Codec: ""},
|
||||||
{Label: "AV1 1440p - 2600k (balanced)", Bitrate: "2600k", Codec: "AV1"},
|
|
||||||
{Label: "H.265 1440p - 3200k (balanced)", Bitrate: "3200k", Codec: "H.265"},
|
|
||||||
{Label: "H.265 1440p - 4000k (noisy sources)", Bitrate: "4000k", Codec: "H.265"},
|
|
||||||
{Label: "AV1 4K - 5M (balanced)", Bitrate: "5000k", Codec: "AV1"},
|
|
||||||
{Label: "H.265 4K - 6M (balanced)", Bitrate: "6000k", Codec: "H.265"},
|
|
||||||
{Label: "AV1 4K - 7M (archive)", Bitrate: "7000k", Codec: "AV1"},
|
|
||||||
{Label: "H.265 4K - 9M (fast/Topaz)", Bitrate: "9000k", Codec: "H.265"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitratePresetLookup := make(map[string]bitratePreset)
|
bitratePresetLookup := make(map[string]bitratePreset)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user