From 2f16d4af36368c7b92f76110461bea4ae2dbf9c5 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Mon, 8 Dec 2025 22:26:06 -0500 Subject: [PATCH] Fallback bitrate uses source bitrate; add size/bitrate delta helpers --- internal/utils/formatting.go | 36 ++++++++++++++++++++++++++++++++++++ main.go | 17 +++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/internal/utils/formatting.go b/internal/utils/formatting.go index 699014f..3af80b0 100644 --- a/internal/utils/formatting.go +++ b/internal/utils/formatting.go @@ -39,6 +39,42 @@ func formatBytes(b int64) string { } } +// DeltaBytes renders size plus delta vs reference. +func DeltaBytes(newBytes, refBytes int64) string { + if newBytes <= 0 { + return "0 B" + } + size := formatBytes(newBytes) + if refBytes <= 0 || refBytes == newBytes { + return size + } + change := float64(newBytes-refBytes) / float64(refBytes) + dir := "increase" + if change < 0 { + dir = "reduction" + } + pct := math.Abs(change) * 100 + return fmt.Sprintf("%s (%.1f%% %s)", size, pct, dir) +} + +// DeltaBitrate renders bitrate plus delta vs reference (expects bps). +func DeltaBitrate(newBps, refBps int) string { + if newBps <= 0 { + return "--" + } + br := formatBitrate(newBps) + if refBps <= 0 || refBps == newBps { + return br + } + change := float64(newBps-refBps) / float64(refBps) + dir := "increase" + if change < 0 { + dir = "reduction" + } + pct := math.Abs(change) * 100 + return fmt.Sprintf("%s (%.1f%% %s)", br, pct, dir) +} + // formatPercent renders a percentage with no trailing zeros after decimal. func formatPercent(val float64) string { val = math.Round(val*10) / 10 // one decimal diff --git a/main.go b/main.go index bec698a..717f7eb 100644 --- a/main.go +++ b/main.go @@ -151,7 +151,10 @@ func getLogsDir() string { } // defaultBitrate picks a sane default when user leaves bitrate empty in bitrate modes. -func defaultBitrate(codec string, width int) string { +func defaultBitrate(codec string, width int, sourceBitrate int) string { + if sourceBitrate > 0 { + return fmt.Sprintf("%dk", sourceBitrate/1000) + } switch strings.ToLower(codec) { case "h.265", "hevc", "libx265", "hevc_nvenc", "hevc_qsv", "hevc_amf", "hevc_videotoolbox": if width >= 1920 { @@ -1212,6 +1215,7 @@ func (s *appState) batchAddToQueue(paths []string) { "outputAspect": s.convert.OutputAspect, "sourceWidth": src.Width, "sourceHeight": src.Height, + "sourceBitrate": src.Bitrate, "sourceDuration": src.Duration, "fieldOrder": src.FieldOrder, } @@ -1330,6 +1334,10 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre cfg := job.Config inputPath := cfg["inputPath"].(string) outputPath := cfg["outputPath"].(string) + sourceBitrate := 0 + if v, ok := cfg["sourceBitrate"].(float64); ok { + sourceBitrate = int(v) + } // If a direct conversion is running, wait until it finishes before starting queued jobs. for s.convertBusy { @@ -1508,6 +1516,11 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre // Aspect ratio conversion sourceWidth, _ := cfg["sourceWidth"].(int) sourceHeight, _ := cfg["sourceHeight"].(int) + // Get source bitrate if present + sourceBitrate = 0 + if v, ok := cfg["sourceBitrate"].(float64); ok { + sourceBitrate = int(v) + } srcAspect := utils.AspectRatioFloat(sourceWidth, sourceHeight) outputAspect, _ := cfg["outputAspect"].(string) aspectHandling, _ := cfg["aspectHandling"].(string) @@ -5515,7 +5528,7 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But // Constant bitrate vb := cfg.VideoBitrate if vb == "" { - vb = defaultBitrate(cfg.VideoCodec, src.Width) + vb = defaultBitrate(cfg.VideoCodec, src.Width, sourceBitrate) } args = append(args, "-b:v", vb, "-minrate", vb, "-maxrate", vb, "-bufsize", vb) } else if cfg.BitrateMode == "VBR" {