feat(upscale): redesign layout and encoding controls
This commit is contained in:
parent
970a2328a9
commit
12610e19b3
441
main.go
441
main.go
|
|
@ -1098,6 +1098,10 @@ type appState struct {
|
|||
upscaleMotionInterpolation bool // Use motion interpolation for frame rate changes
|
||||
upscaleBlurEnabled bool // Apply blur in upscale pipeline
|
||||
upscaleBlurSigma float64 // Blur strength (sigma)
|
||||
upscaleEncoderPreset string // libx264 preset for upscale output
|
||||
upscaleBitrateMode string // CRF, CBR, VBR
|
||||
upscaleBitratePreset string // preset label for bitrate modes
|
||||
upscaleManualBitrate string // manual bitrate value (e.g., 2500k)
|
||||
|
||||
// Snippet settings
|
||||
snippetLength int // Length of snippet in seconds (default: 20)
|
||||
|
|
@ -1121,6 +1125,8 @@ type appState struct {
|
|||
authorRegion string // "NTSC", "PAL", "AUTO"
|
||||
authorAspectRatio string // "4:3", "16:9", "AUTO"
|
||||
authorCreateMenu bool // Whether to create DVD menu
|
||||
authorMenuTemplate string // "Simple", "Dark", "Poster"
|
||||
authorMenuBackgroundImage string // Path to a user-selected background image
|
||||
authorTitle string // DVD title
|
||||
authorSubtitles []string // Subtitle file paths
|
||||
authorAudioTracks []string // Additional audio tracks
|
||||
|
|
@ -5558,6 +5564,10 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
|
|||
useMotionInterp, _ := cfg["useMotionInterpolation"].(bool)
|
||||
sourceFrameRate := toFloat(cfg["sourceFrameRate"])
|
||||
qualityPreset, _ := cfg["qualityPreset"].(string)
|
||||
encoderPreset, _ := cfg["encoderPreset"].(string)
|
||||
bitrateMode, _ := cfg["bitrateMode"].(string)
|
||||
bitratePreset, _ := cfg["bitratePreset"].(string)
|
||||
manualBitrate, _ := cfg["manualBitrate"].(string)
|
||||
blurEnabled, _ := cfg["blurEnabled"].(bool)
|
||||
blurSigma := toFloat(cfg["blurSigma"])
|
||||
|
||||
|
|
@ -5590,6 +5600,91 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
|
|||
crfValue = 16
|
||||
}
|
||||
|
||||
resolveBitrate := func() string {
|
||||
if strings.TrimSpace(manualBitrate) != "" {
|
||||
return manualBitrate
|
||||
}
|
||||
switch bitratePreset {
|
||||
case "0.5 Mbps - Ultra Low":
|
||||
return "500k"
|
||||
case "1.0 Mbps - Very Low":
|
||||
return "1000k"
|
||||
case "1.5 Mbps - Low":
|
||||
return "1500k"
|
||||
case "2.0 Mbps - Medium-Low":
|
||||
return "2000k"
|
||||
case "2.5 Mbps - Medium":
|
||||
return "2500k"
|
||||
case "4.0 Mbps - Good":
|
||||
return "4000k"
|
||||
case "6.0 Mbps - High":
|
||||
return "6000k"
|
||||
case "8.0 Mbps - Very High":
|
||||
return "8000k"
|
||||
default:
|
||||
return "2500k"
|
||||
}
|
||||
}
|
||||
|
||||
parseBitrateKbps := func(val string) int {
|
||||
v := strings.TrimSpace(strings.ToLower(val))
|
||||
if v == "" {
|
||||
return 0
|
||||
}
|
||||
mult := 1.0
|
||||
if strings.HasSuffix(v, "k") {
|
||||
v = strings.TrimSuffix(v, "k")
|
||||
mult = 1
|
||||
} else if strings.HasSuffix(v, "m") {
|
||||
v = strings.TrimSuffix(v, "m")
|
||||
mult = 1000
|
||||
}
|
||||
num, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return int(num * mult)
|
||||
}
|
||||
|
||||
normalizeBitrateMode := func(mode string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(strings.ToUpper(mode), "CBR"):
|
||||
return "CBR"
|
||||
case strings.HasPrefix(strings.ToUpper(mode), "VBR"):
|
||||
return "VBR"
|
||||
default:
|
||||
return "CRF"
|
||||
}
|
||||
}
|
||||
|
||||
appendEncodingArgs := func(args []string) []string {
|
||||
preset := encoderPreset
|
||||
if strings.TrimSpace(preset) == "" {
|
||||
preset = "slow"
|
||||
}
|
||||
mode := normalizeBitrateMode(bitrateMode)
|
||||
args = append(args, "-preset", preset)
|
||||
switch mode {
|
||||
case "CBR", "VBR":
|
||||
bitrateVal := resolveBitrate()
|
||||
args = append(args, "-b:v", bitrateVal)
|
||||
kbps := parseBitrateKbps(bitrateVal)
|
||||
if kbps > 0 {
|
||||
maxrate := kbps
|
||||
bufsize := kbps * 2
|
||||
if mode == "VBR" {
|
||||
maxrate = kbps * 2
|
||||
bufsize = kbps * 4
|
||||
}
|
||||
args = append(args, "-maxrate", fmt.Sprintf("%dk", maxrate))
|
||||
args = append(args, "-bufsize", fmt.Sprintf("%dk", bufsize))
|
||||
}
|
||||
default:
|
||||
args = append(args, "-crf", strconv.Itoa(crfValue))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// Build filter chain
|
||||
var baseFilters []string
|
||||
|
||||
|
|
@ -5849,10 +5944,9 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
|
|||
reassembleArgs = append(reassembleArgs, "-vf", finalScale)
|
||||
}
|
||||
|
||||
reassembleArgs = append(reassembleArgs, "-c:v", "libx264")
|
||||
reassembleArgs = appendEncodingArgs(reassembleArgs)
|
||||
reassembleArgs = append(reassembleArgs,
|
||||
"-c:v", "libx264",
|
||||
"-preset", "slow",
|
||||
"-crf", strconv.Itoa(crfValue),
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-c:a", "copy",
|
||||
"-shortest",
|
||||
|
|
@ -5907,11 +6001,10 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
|
|||
args = append(args, "-vf", vfilter)
|
||||
}
|
||||
|
||||
// Use lossless MKV by default for upscales; copy audio
|
||||
// Use MKV container by default; copy audio
|
||||
args = append(args, "-c:v", "libx264")
|
||||
args = appendEncodingArgs(args)
|
||||
args = append(args,
|
||||
"-c:v", "libx264",
|
||||
"-preset", "slow",
|
||||
"-crf", strconv.Itoa(crfValue),
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-c:a", "copy",
|
||||
"-progress", "pipe:1",
|
||||
|
|
@ -14929,6 +15022,18 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
if state.upscaleQualityPreset == "" {
|
||||
state.upscaleQualityPreset = "Near-lossless (CRF 16)"
|
||||
}
|
||||
if state.upscaleEncoderPreset == "" {
|
||||
state.upscaleEncoderPreset = "slow"
|
||||
}
|
||||
if state.upscaleBitrateMode == "" {
|
||||
state.upscaleBitrateMode = "CRF"
|
||||
}
|
||||
if state.upscaleBitratePreset == "" {
|
||||
state.upscaleBitratePreset = "2.5 Mbps - Medium"
|
||||
}
|
||||
if state.upscaleManualBitrate == "" {
|
||||
state.upscaleManualBitrate = "2500k"
|
||||
}
|
||||
if state.upscaleAIPreset == "" {
|
||||
state.upscaleAIPreset = "Balanced"
|
||||
state.upscaleAIScale = 4.0
|
||||
|
|
@ -15007,7 +15112,23 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
state.showFiltersView()
|
||||
})
|
||||
|
||||
// Traditional Scaling Section
|
||||
mediumBlue := utils.MustHex("#13182B")
|
||||
navyBlue := utils.MustHex("#191F35")
|
||||
|
||||
buildUpscaleBox := func(title string, content fyne.CanvasObject) fyne.CanvasObject {
|
||||
bg := canvas.NewRectangle(navyBlue)
|
||||
bg.CornerRadius = 10
|
||||
bg.StrokeColor = gridColor
|
||||
bg.StrokeWidth = 1
|
||||
body := container.NewVBox(
|
||||
widget.NewLabelWithStyle(title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
widget.NewSeparator(),
|
||||
content,
|
||||
)
|
||||
return container.NewMax(bg, container.NewPadded(body))
|
||||
}
|
||||
|
||||
// Scaling (method + blur)
|
||||
methodLabel := widget.NewLabel(fmt.Sprintf("Method: %s", state.upscaleMethod))
|
||||
methodSelect := widget.NewSelect([]string{
|
||||
"lanczos", // Sharp, best general purpose
|
||||
|
|
@ -15024,62 +15145,6 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
methodInfo.TextStyle = fyne.TextStyle{Italic: true}
|
||||
methodInfo.Wrapping = fyne.TextWrapWord
|
||||
|
||||
traditionalSection := widget.NewCard("Traditional Scaling (FFmpeg)", "", container.NewVBox(
|
||||
widget.NewLabel("Classic upscaling methods - always available"),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Scaling Algorithm:"),
|
||||
methodSelect,
|
||||
),
|
||||
methodLabel,
|
||||
widget.NewSeparator(),
|
||||
methodInfo,
|
||||
))
|
||||
|
||||
// Resolution Selection Section
|
||||
resLabel := widget.NewLabel(fmt.Sprintf("Target: %s", state.upscaleTargetRes))
|
||||
resSelect := widget.NewSelect([]string{
|
||||
"Match Source",
|
||||
"2X (relative)",
|
||||
"4X (relative)",
|
||||
"720p (1280x720)",
|
||||
"1080p (1920x1080)",
|
||||
"1440p (2560x1440)",
|
||||
"4K (3840x2160)",
|
||||
"8K (7680x4320)",
|
||||
"Custom",
|
||||
}, func(s string) {
|
||||
state.upscaleTargetRes = s
|
||||
resLabel.SetText(fmt.Sprintf("Target: %s", s))
|
||||
})
|
||||
resSelect.SetSelected(state.upscaleTargetRes)
|
||||
|
||||
resolutionSection := widget.NewCard("Target Resolution", "", container.NewVBox(
|
||||
widget.NewLabel("Select output resolution"),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Resolution:"),
|
||||
resSelect,
|
||||
),
|
||||
resLabel,
|
||||
sourceResLabel,
|
||||
))
|
||||
|
||||
qualitySelect := widget.NewSelect([]string{
|
||||
"Lossless (CRF 0)",
|
||||
"Near-lossless (CRF 16)",
|
||||
"High (CRF 18)",
|
||||
}, func(s string) {
|
||||
state.upscaleQualityPreset = s
|
||||
})
|
||||
qualitySelect.SetSelected(state.upscaleQualityPreset)
|
||||
|
||||
qualitySection := widget.NewCard("Output Quality", "", container.NewVBox(
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Quality:"),
|
||||
qualitySelect,
|
||||
),
|
||||
widget.NewLabel("Lower CRF = higher quality/larger files"),
|
||||
))
|
||||
|
||||
blurLabel := widget.NewLabel(fmt.Sprintf("Blur Strength: %.2f", state.upscaleBlurSigma))
|
||||
blurSlider := widget.NewSlider(0.0, 8.0)
|
||||
blurSlider.Step = 0.1
|
||||
|
|
@ -15104,13 +15169,165 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
blurSlider.Disable()
|
||||
}
|
||||
|
||||
blurSection := widget.NewCard("Blur (Optional)", "", container.NewVBox(
|
||||
widget.NewLabel("Apply a soft blur during upscale processing"),
|
||||
blurCheck,
|
||||
container.NewVBox(blurLabel, blurSlider),
|
||||
// Resolution
|
||||
resLabel := widget.NewLabel(fmt.Sprintf("Target: %s", state.upscaleTargetRes))
|
||||
resSelect := widget.NewSelect([]string{
|
||||
"Match Source",
|
||||
"2X (relative)",
|
||||
"4X (relative)",
|
||||
"720p (1280x720)",
|
||||
"1080p (1920x1080)",
|
||||
"1440p (2560x1440)",
|
||||
"4K (3840x2160)",
|
||||
"8K (7680x4320)",
|
||||
"Custom",
|
||||
}, func(s string) {
|
||||
state.upscaleTargetRes = s
|
||||
resLabel.SetText(fmt.Sprintf("Target: %s", s))
|
||||
})
|
||||
resSelect.SetSelected(state.upscaleTargetRes)
|
||||
|
||||
resolutionSection := buildUpscaleBox("Target Resolution", container.NewVBox(
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Resolution:"),
|
||||
resSelect,
|
||||
),
|
||||
resLabel,
|
||||
sourceResLabel,
|
||||
))
|
||||
|
||||
// Frame Rate Section
|
||||
// Video Encoding
|
||||
qualitySelect := widget.NewSelect([]string{
|
||||
"Lossless (CRF 0)",
|
||||
"Near-lossless (CRF 16)",
|
||||
"High (CRF 18)",
|
||||
}, func(s string) {
|
||||
state.upscaleQualityPreset = s
|
||||
})
|
||||
qualitySelect.SetSelected(state.upscaleQualityPreset)
|
||||
|
||||
encoderPresetSelect := widget.NewSelect([]string{
|
||||
"ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow",
|
||||
}, func(s string) {
|
||||
state.upscaleEncoderPreset = s
|
||||
})
|
||||
encoderPresetSelect.SetSelected(state.upscaleEncoderPreset)
|
||||
|
||||
var updateEncodingVisibility func()
|
||||
|
||||
bitrateModeSelect := widget.NewSelect([]string{
|
||||
"CRF (Constant Rate Factor)",
|
||||
"CBR (Constant Bitrate)",
|
||||
"VBR (Variable Bitrate)",
|
||||
}, func(s string) {
|
||||
switch {
|
||||
case strings.HasPrefix(s, "CRF"):
|
||||
state.upscaleBitrateMode = "CRF"
|
||||
case strings.HasPrefix(s, "CBR"):
|
||||
state.upscaleBitrateMode = "CBR"
|
||||
case strings.HasPrefix(s, "VBR"):
|
||||
state.upscaleBitrateMode = "VBR"
|
||||
default:
|
||||
state.upscaleBitrateMode = s
|
||||
}
|
||||
if updateEncodingVisibility != nil {
|
||||
updateEncodingVisibility()
|
||||
}
|
||||
})
|
||||
switch state.upscaleBitrateMode {
|
||||
case "CBR":
|
||||
bitrateModeSelect.SetSelected("CBR (Constant Bitrate)")
|
||||
case "VBR":
|
||||
bitrateModeSelect.SetSelected("VBR (Variable Bitrate)")
|
||||
default:
|
||||
bitrateModeSelect.SetSelected("CRF (Constant Rate Factor)")
|
||||
}
|
||||
|
||||
type bitratePreset struct {
|
||||
Label string
|
||||
Bitrate string
|
||||
}
|
||||
presets := []bitratePreset{
|
||||
{Label: "0.5 Mbps - Ultra Low", Bitrate: "500k"},
|
||||
{Label: "1.0 Mbps - Very Low", Bitrate: "1000k"},
|
||||
{Label: "1.5 Mbps - Low", Bitrate: "1500k"},
|
||||
{Label: "2.0 Mbps - Medium-Low", Bitrate: "2000k"},
|
||||
{Label: "2.5 Mbps - Medium", Bitrate: "2500k"},
|
||||
{Label: "4.0 Mbps - Good", Bitrate: "4000k"},
|
||||
{Label: "6.0 Mbps - High", Bitrate: "6000k"},
|
||||
{Label: "8.0 Mbps - Very High", Bitrate: "8000k"},
|
||||
{Label: "Manual", Bitrate: ""},
|
||||
}
|
||||
bitratePresetLookup := make(map[string]bitratePreset)
|
||||
var bitratePresetLabels []string
|
||||
for _, p := range presets {
|
||||
bitratePresetLookup[p.Label] = p
|
||||
bitratePresetLabels = append(bitratePresetLabels, p.Label)
|
||||
}
|
||||
|
||||
manualBitrateEntry := widget.NewEntry()
|
||||
manualBitrateEntry.SetPlaceHolder("e.g., 2500k")
|
||||
manualBitrateEntry.SetText(state.upscaleManualBitrate)
|
||||
manualBitrateEntry.OnChanged = func(val string) {
|
||||
state.upscaleManualBitrate = val
|
||||
}
|
||||
|
||||
bitratePresetSelect := widget.NewSelect(bitratePresetLabels, func(s string) {
|
||||
state.upscaleBitratePreset = s
|
||||
preset := bitratePresetLookup[s]
|
||||
if preset.Bitrate == "" {
|
||||
manualBitrateEntry.Show()
|
||||
} else {
|
||||
state.upscaleManualBitrate = preset.Bitrate
|
||||
manualBitrateEntry.SetText(preset.Bitrate)
|
||||
manualBitrateEntry.Hide()
|
||||
}
|
||||
})
|
||||
bitratePresetSelect.SetSelected(state.upscaleBitratePreset)
|
||||
if bitratePresetLookup[state.upscaleBitratePreset].Bitrate == "" {
|
||||
manualBitrateEntry.Show()
|
||||
} else {
|
||||
manualBitrateEntry.Hide()
|
||||
}
|
||||
|
||||
updateEncodingVisibility = func() {
|
||||
mode := state.upscaleBitrateMode
|
||||
if mode == "" || mode == "CRF" {
|
||||
qualitySelect.Enable()
|
||||
bitratePresetSelect.Hide()
|
||||
manualBitrateEntry.Hide()
|
||||
} else {
|
||||
qualitySelect.Disable()
|
||||
bitratePresetSelect.Show()
|
||||
if bitratePresetLookup[state.upscaleBitratePreset].Bitrate == "" {
|
||||
manualBitrateEntry.Show()
|
||||
}
|
||||
}
|
||||
}
|
||||
updateEncodingVisibility()
|
||||
|
||||
encodingSection := buildUpscaleBox("Video Encoding", container.NewVBox(
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Encoder Preset:"),
|
||||
encoderPresetSelect,
|
||||
),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Quality Preset:"),
|
||||
qualitySelect,
|
||||
),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Bitrate Mode:"),
|
||||
bitrateModeSelect,
|
||||
),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Bitrate Preset:"),
|
||||
bitratePresetSelect,
|
||||
),
|
||||
manualBitrateEntry,
|
||||
widget.NewLabel("CRF mode controls quality; bitrate modes control size."),
|
||||
))
|
||||
|
||||
// Frame Rate
|
||||
frameRateLabel := widget.NewLabel(fmt.Sprintf("Frame Rate: %s", state.upscaleFrameRate))
|
||||
frameRateSelect := widget.NewSelect([]string{"Source", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60"}, func(s string) {
|
||||
state.upscaleFrameRate = s
|
||||
|
|
@ -15123,8 +15340,7 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
})
|
||||
motionInterpCheck.SetChecked(state.upscaleMotionInterpolation)
|
||||
|
||||
frameRateSection := widget.NewCard("Frame Rate", "", container.NewVBox(
|
||||
widget.NewLabel("Convert frame rate (optional)"),
|
||||
frameRateSection := buildUpscaleBox("Frame Rate", container.NewVBox(
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Target FPS:"),
|
||||
frameRateSelect,
|
||||
|
|
@ -15140,8 +15356,8 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
aiModelLabel = aiModelOptions[0]
|
||||
}
|
||||
|
||||
// AI Upscaling Section
|
||||
var aiSection *widget.Card
|
||||
// AI Upscaling Section (nested under Scaling)
|
||||
var aiContent fyne.CanvasObject
|
||||
if state.upscaleAIAvailable {
|
||||
var aiTileSelect *widget.Select
|
||||
var aiTTACheck *widget.Check
|
||||
|
|
@ -15350,7 +15566,7 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
denoiseHint.TextStyle = fyne.TextStyle{Italic: true}
|
||||
updateDenoiseAvailability(state.upscaleAIModel)
|
||||
|
||||
aiSection = widget.NewCard("AI Upscaling", "✓ Available", container.NewVBox(
|
||||
aiContent = container.NewVBox(
|
||||
widget.NewLabel("Real-ESRGAN detected - enhanced quality available"),
|
||||
aiEnabledCheck,
|
||||
container.NewGridWithColumns(2,
|
||||
|
|
@ -15388,26 +15604,49 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
container.NewGridWithColumns(3, aiThreadsLoad, aiThreadsProc, aiThreadsSave),
|
||||
),
|
||||
widget.NewLabel("Note: AI upscaling is slower but produces higher quality results"),
|
||||
))
|
||||
)
|
||||
} else {
|
||||
backendNote := "Real-ESRGAN not detected. Install for enhanced quality:"
|
||||
if state.upscaleAIBackend == "python" {
|
||||
backendNote = "Python Real-ESRGAN detected, but the ncnn backend is required for now."
|
||||
}
|
||||
aiSection = widget.NewCard("AI Upscaling", "Not Available", container.NewVBox(
|
||||
aiContent = container.NewVBox(
|
||||
widget.NewLabel(backendNote),
|
||||
widget.NewLabel("https://github.com/xinntao/Real-ESRGAN"),
|
||||
widget.NewLabel("Traditional scaling methods will be used."),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
aiSection := container.NewVBox(
|
||||
widget.NewLabelWithStyle("AI Upscaling", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
widget.NewSeparator(),
|
||||
aiContent,
|
||||
)
|
||||
|
||||
traditionalSection := buildUpscaleBox("Scaling", container.NewVBox(
|
||||
widget.NewLabel("Classic upscaling methods - always available"),
|
||||
container.NewGridWithColumns(2,
|
||||
widget.NewLabel("Scaling Algorithm:"),
|
||||
methodSelect,
|
||||
),
|
||||
methodLabel,
|
||||
widget.NewSeparator(),
|
||||
methodInfo,
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Optional blur"),
|
||||
blurCheck,
|
||||
container.NewVBox(blurLabel, blurSlider),
|
||||
widget.NewSeparator(),
|
||||
aiSection,
|
||||
))
|
||||
|
||||
// Filter Integration Section
|
||||
applyFiltersCheck := widget.NewCheck("Apply filters before upscaling", func(checked bool) {
|
||||
state.upscaleApplyFilters = checked
|
||||
})
|
||||
applyFiltersCheck.SetChecked(state.upscaleApplyFilters)
|
||||
|
||||
filterIntegrationSection := widget.NewCard("Filter Integration", "", container.NewVBox(
|
||||
filterIntegrationSection := buildUpscaleBox("Filter Integration", container.NewVBox(
|
||||
widget.NewLabel("Apply color correction and filters from Filters module"),
|
||||
applyFiltersCheck,
|
||||
widget.NewLabel("Filters will be applied before upscaling for best quality"),
|
||||
|
|
@ -15452,6 +15691,10 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
"inputPath": state.upscaleFile.Path,
|
||||
"outputPath": outputPath,
|
||||
"method": state.upscaleMethod,
|
||||
"encoderPreset": state.upscaleEncoderPreset,
|
||||
"bitrateMode": state.upscaleBitrateMode,
|
||||
"bitratePreset": state.upscaleBitratePreset,
|
||||
"manualBitrate": state.upscaleManualBitrate,
|
||||
"targetWidth": float64(targetWidth),
|
||||
"targetHeight": float64(targetHeight),
|
||||
"targetPreset": state.upscaleTargetRes,
|
||||
|
|
@ -15521,22 +15764,38 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
addQueueBtn.Importance = widget.MediumImportance
|
||||
|
||||
// Main content
|
||||
spacing := func() fyne.CanvasObject {
|
||||
spacer := canvas.NewRectangle(color.Transparent)
|
||||
spacer.SetMinSize(fyne.NewSize(0, 10))
|
||||
return spacer
|
||||
}
|
||||
|
||||
metaPanel, _ := buildMetadataPanel(state, state.upscaleFile, fyne.NewSize(0, 200))
|
||||
|
||||
leftPanel := container.NewVBox(
|
||||
instructions,
|
||||
widget.NewSeparator(),
|
||||
fileLabel,
|
||||
loadBtn,
|
||||
filtersNavBtn,
|
||||
spacing(),
|
||||
buildUpscaleBox("Video", container.NewVBox(
|
||||
fileLabel,
|
||||
loadBtn,
|
||||
filtersNavBtn,
|
||||
videoContainer,
|
||||
)),
|
||||
spacing(),
|
||||
metaPanel,
|
||||
)
|
||||
|
||||
settingsPanel := container.NewVBox(
|
||||
traditionalSection,
|
||||
spacing(),
|
||||
resolutionSection,
|
||||
qualitySection,
|
||||
blurSection,
|
||||
spacing(),
|
||||
encodingSection,
|
||||
spacing(),
|
||||
frameRateSection,
|
||||
aiSection,
|
||||
spacing(),
|
||||
filterIntegrationSection,
|
||||
spacing(),
|
||||
container.NewGridWithColumns(2, applyBtn, addQueueBtn),
|
||||
)
|
||||
|
||||
|
|
@ -15544,12 +15803,14 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
|
|||
// Adaptive height for small screens
|
||||
// Avoid rigid min sizes so window snapping works across modules.
|
||||
|
||||
mainContent := container.New(&fixedHSplitLayout{ratio: 0.6},
|
||||
container.NewVBox(leftPanel, videoContainer),
|
||||
settingsScroll,
|
||||
)
|
||||
split := container.NewHSplit(leftPanel, settingsScroll)
|
||||
split.Offset = 0.58
|
||||
mainContent := split
|
||||
|
||||
content := container.NewPadded(mainContent)
|
||||
content := container.NewMax(
|
||||
canvas.NewRectangle(mediumBlue),
|
||||
container.NewPadded(mainContent),
|
||||
)
|
||||
|
||||
return container.NewBorder(topBar, bottomBar, nil, nil, content)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user