Fallback bitrate uses source bitrate; add size/bitrate delta helpers

This commit is contained in:
Stu Leak 2025-12-08 22:26:06 -05:00
parent fce78e0acb
commit 2f16d4af36
2 changed files with 51 additions and 2 deletions

View File

@ -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

17
main.go
View File

@ -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" {