From bd49952800cfa23183644c37b9fa5ff1be2eadf5 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Mon, 8 Dec 2025 18:46:34 -0500 Subject: [PATCH] Normalize MP4 format label, improve log readability, and prep reduction display --- internal/convert/ffmpeg.go | 2 +- internal/convert/format_name.go | 21 ++++++++++++++++++++ internal/utils/formatting.go | 27 +++++++++++++++++++++++++ internal/utils/utils.go | 1 - main.go | 35 ++++++++++++++++++++++++++------- 5 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 internal/convert/format_name.go create mode 100644 internal/utils/formatting.go diff --git a/internal/convert/ffmpeg.go b/internal/convert/ffmpeg.go index d150a68..7f592e2 100644 --- a/internal/convert/ffmpeg.go +++ b/internal/convert/ffmpeg.go @@ -145,7 +145,7 @@ func ProbeVideo(path string) (*VideoSource, error) { src := &VideoSource{ Path: path, DisplayName: filepath.Base(path), - Format: utils.FirstNonEmpty(result.Format.Format, result.Format.FormatName), + Format: humanFriendlyFormat(result.Format.Format, result.Format.FormatName), } if rate, err := utils.ParseInt(result.Format.BitRate); err == nil { src.Bitrate = rate diff --git a/internal/convert/format_name.go b/internal/convert/format_name.go new file mode 100644 index 0000000..f509c78 --- /dev/null +++ b/internal/convert/format_name.go @@ -0,0 +1,21 @@ +package convert + +import "strings" + +// humanFriendlyFormat normalizes format names to less confusing labels. +func humanFriendlyFormat(format, formatLong string) string { + f := strings.ToLower(strings.TrimSpace(format)) + fl := strings.ToLower(strings.TrimSpace(formatLong)) + + // Treat common QuickTime/MOV wording as MP4 when the extension is typically mp4 + if strings.Contains(f, "mov") || strings.Contains(fl, "quicktime") { + return "MP4" + } + if f != "" { + return format + } + if formatLong != "" { + return formatLong + } + return "Unknown" +} diff --git a/internal/utils/formatting.go b/internal/utils/formatting.go new file mode 100644 index 0000000..ed36dca --- /dev/null +++ b/internal/utils/formatting.go @@ -0,0 +1,27 @@ +package utils + +import "math" + +// ReductionText returns a string like "965 MB (24% reduction)" given original bytes and new bytes. +func ReductionText(origBytes, newBytes int64) string { + if origBytes <= 0 || newBytes <= 0 { + return "" + } + if newBytes >= origBytes { + return "" + } + reduction := 100.0 * (1.0 - float64(newBytes)/float64(origBytes)) + if reduction <= 0 { + return "" + } + return formatFileSize(newBytes) + " (" + formatPercent(reduction) + " reduction)" +} + +// formatPercent renders a percentage with no trailing zeros after decimal. +func formatPercent(val float64) string { + val = math.Round(val*10) / 10 // one decimal + if val == math.Trunc(val) { + return formatInt(int(val)) + "%" + } + return formatFloat(val) + "%" +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index d5eec5d..83a25ca 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -3,7 +3,6 @@ package utils import ( "fmt" "image/color" - "math" "os" "path/filepath" "strconv" diff --git a/main.go b/main.go index a576049..bf4b047 100644 --- a/main.go +++ b/main.go @@ -165,9 +165,11 @@ func (s *appState) openLogViewer(title, path string, live bool) { text := widget.NewMultiLineEntry() text.SetText(string(data)) text.Wrapping = fyne.TextWrapWord + text.TextStyle = fyne.TextStyle{Monospace: true} text.Disable() - scroll := container.NewVScroll(text) - scroll.SetMinSize(fyne.NewSize(600, 400)) + bg := canvas.NewRectangle(color.NRGBA{0x15, 0x1a, 0x24, 0xff}) // slightly lighter than app bg + scroll := container.NewVScroll(container.NewMax(bg, text)) + scroll.SetMinSize(fyne.NewSize(700, 450)) stop := make(chan struct{}) if live { @@ -195,8 +197,13 @@ func (s *appState) openLogViewer(title, path string, live bool) { closeBtn := widget.NewButton("Close", func() { close(stop) }) - content := container.NewBorder(nil, container.NewHBox(layout.NewSpacer(), closeBtn), nil, nil, scroll) - d := dialog.NewCustomWithoutButtons(title, content, s.window) + copyBtn := widget.NewButton("Copy All", func() { + s.window.Clipboard().SetContent(text.Text) + }) + buttons := container.NewHBox(copyBtn, layout.NewSpacer(), closeBtn) + content := container.NewBorder(nil, buttons, nil, nil, scroll) + d := dialog.NewCustom(title, "Close", content, s.window) + d.SetOnClosed(func() { close(stop) }) d.Show() } @@ -3596,6 +3603,9 @@ func buildMetadataPanel(state *appState, src *videoSource, min fyne.Size) (fyne. } colorSpace := utils.FirstNonEmpty(src.ColorSpace, "Unknown") + if strings.EqualFold(colorSpace, "unknown") && strings.Contains(strings.ToLower(src.Format), "mp4") { + colorSpace = "MP4 (ISO BMFF family)" + } colorRange := utils.FirstNonEmpty(src.ColorRange, "Unknown") if colorRange == "tv" { colorRange = "Limited (TV)" @@ -3668,7 +3678,7 @@ Metadata: %s`, info := widget.NewForm( widget.NewFormItem("File", widget.NewLabel(src.DisplayName)), - widget.NewFormItem("Format", widget.NewLabel(utils.FirstNonEmpty(src.Format, "Unknown"))), + widget.NewFormItem("Format Family", widget.NewLabel(utils.FirstNonEmpty(src.Format, "Unknown"))), widget.NewFormItem("Resolution", widget.NewLabel(fmt.Sprintf("%dx%d", src.Width, src.Height))), widget.NewFormItem("Aspect Ratio", widget.NewLabel(src.AspectRatioString())), widget.NewFormItem("Pixel Aspect Ratio", widget.NewLabel(par)), @@ -3691,6 +3701,7 @@ Metadata: %s`, for _, item := range info.Items { if lbl, ok := item.Widget.(*widget.Label); ok { lbl.Wrapping = fyne.TextWrapWord + lbl.TextStyle = fyne.TextStyle{} // prevent selection } } @@ -6786,14 +6797,18 @@ func buildCompareView(state *appState) fyne.CanvasObject { // Info labels file1Info := widget.NewLabel("No file loaded") file1Info.Wrapping = fyne.TextWrapWord + file1Info.TextStyle = fyne.TextStyle{} // non-selectable label file2Info := widget.NewLabel("No file loaded") file2Info.Wrapping = fyne.TextWrapWord + file2Info.TextStyle = fyne.TextStyle{} // non-selectable label // Helper function to format metadata formatMetadata := func(src *videoSource) string { fileSize := "Unknown" + origBytes := int64(0) if fi, err := os.Stat(src.Path); err == nil { + origBytes = fi.Size() sizeMB := float64(fi.Size()) / (1024 * 1024) if sizeMB >= 1024 { fileSize = fmt.Sprintf("%.2f GB", sizeMB/1024) @@ -6801,12 +6816,18 @@ func buildCompareView(state *appState) fyne.CanvasObject { fileSize = fmt.Sprintf("%.2f MB", sizeMB) } } + var bitrateStr string + if src.Bitrate > 0 { + bitrateStr = formatBitrate(src.Bitrate) + } else { + bitrateStr = "--" + } return fmt.Sprintf( "━━━ FILE INFO ━━━\n"+ "Path: %s\n"+ "File Size: %s\n"+ - "Format: %s\n"+ + "Format Family: %s\n"+ "\n━━━ VIDEO ━━━\n"+ "Codec: %s\n"+ "Resolution: %dx%d\n"+ @@ -6835,7 +6856,7 @@ func buildCompareView(state *appState) fyne.CanvasObject { src.Width, src.Height, src.AspectRatioString(), src.FrameRate, - formatBitrate(src.Bitrate), + bitrateStr, src.PixelFormat, src.ColorSpace, src.ColorRange,