diff --git a/DONE.md b/DONE.md index 56c83da..c15c830 100644 --- a/DONE.md +++ b/DONE.md @@ -767,6 +767,7 @@ This file tracks completed features, fixes, and milestones. - ✅ Fixed aspect ratio default from 16:9 to Source (dev7) - ✅ Ranked benchmark results by score and added cancel confirmation - ✅ Added estimated audio bitrate fallback when metadata is missing +- ✅ Made target file size input unit-selectable with numeric-only entry - ✅ Stabilized video seeking and embedded rendering - ✅ Improved player window positioning - ✅ Fixed clear video functionality diff --git a/TODO.md b/TODO.md index d0a493a..3ed72d9 100644 --- a/TODO.md +++ b/TODO.md @@ -42,6 +42,7 @@ This file tracks upcoming features, improvements, and known issues. - Dynamic dropdown based on codec - Lossless + Target Size mode support - Audio bitrate estimation when metadata is missing + - Target size unit selector and numeric entry ## Priority Features for dev20+ diff --git a/internal/convert/types.go b/internal/convert/types.go index e61ad5f..7f3fdd9 100644 --- a/internal/convert/types.go +++ b/internal/convert/types.go @@ -217,15 +217,18 @@ func ParseFileSize(sizeStr string) (int64, error) { if err != nil { return 0, fmt.Errorf("invalid size format: %s", sizeStr) } + if unit == "" { + unit = "MB" + } // Convert to bytes multiplier := int64(1) switch unit { - case "KB": + case "K", "KB": multiplier = 1024 - case "MB": + case "M", "MB": multiplier = 1024 * 1024 - case "GB": + case "G", "GB": multiplier = 1024 * 1024 * 1024 case "B", "": multiplier = 1 diff --git a/main.go b/main.go index 9d79777..d1b4839 100644 --- a/main.go +++ b/main.go @@ -5966,7 +5966,61 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject { // Target File Size with smart presets + manual entry targetFileSizeEntry = widget.NewEntry() - targetFileSizeEntry.SetPlaceHolder("e.g., 25MB, 100MB, 8MB") + targetFileSizeEntry.SetPlaceHolder("e.g., 250") + targetFileSizeUnitSelect := widget.NewSelect([]string{"KB", "MB", "GB"}, func(value string) {}) + targetFileSizeUnitSelect.SetSelected("MB") + targetSizeManualRow := container.NewBorder(nil, nil, nil, targetFileSizeUnitSelect, targetFileSizeEntry) + + parseSizeParts := func(input string) (string, string, bool) { + trimmed := strings.TrimSpace(input) + if trimmed == "" { + return "", "", false + } + upper := strings.ToUpper(trimmed) + var num float64 + var unit string + if _, err := fmt.Sscanf(upper, "%f%s", &num, &unit); err != nil { + return "", "", false + } + numStr := strconv.FormatFloat(num, 'f', -1, 64) + return numStr, unit, true + } + + updateTargetSizeState := func() { + val := strings.TrimSpace(targetFileSizeEntry.Text) + if val == "" { + state.convert.TargetFileSize = "" + return + } + if num, unit, ok := parseSizeParts(val); ok && unit != "" { + if num != val { + targetFileSizeEntry.SetText(num) + return + } + if unit != targetFileSizeUnitSelect.Selected { + targetFileSizeUnitSelect.SetSelected(unit) + return + } + val = num + } + unit := targetFileSizeUnitSelect.Selected + if unit == "" { + unit = "MB" + targetFileSizeUnitSelect.SetSelected(unit) + } + state.convert.TargetFileSize = val + unit + logging.Debug(logging.CatUI, "target file size set to %s", state.convert.TargetFileSize) + if buildCommandPreview != nil { + buildCommandPreview() + } + } + + targetFileSizeUnitSelect.OnChanged = func(value string) { + if targetFileSizeEntry.Hidden { + return + } + updateTargetSizeState() + } updateTargetSizeOptions := func() { if src == nil { @@ -6010,8 +6064,17 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject { targetFileSizeSelect = widget.NewSelect([]string{"Manual", "25MB", "50MB", "100MB", "200MB", "500MB", "1GB"}, func(value string) { if value == "Manual" { - targetFileSizeEntry.Show() - targetFileSizeEntry.SetText(state.convert.TargetFileSize) + targetSizeManualRow.Show() + if state.convert.TargetFileSize != "" { + if num, unit, ok := parseSizeParts(state.convert.TargetFileSize); ok { + targetFileSizeEntry.SetText(num) + if unit != "" { + targetFileSizeUnitSelect.SetSelected(unit) + } + } else { + targetFileSizeEntry.SetText(state.convert.TargetFileSize) + } + } } else { // Extract size from selection (handle "XMB (Y% smaller)" format) var sizeStr string @@ -6023,19 +6086,32 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject { sizeStr = value } state.convert.TargetFileSize = sizeStr - targetFileSizeEntry.SetText(sizeStr) - targetFileSizeEntry.Hide() + if num, unit, ok := parseSizeParts(sizeStr); ok { + targetFileSizeEntry.SetText(num) + if unit != "" { + targetFileSizeUnitSelect.SetSelected(unit) + } + } else { + targetFileSizeEntry.SetText(sizeStr) + } + targetSizeManualRow.Hide() } logging.Debug(logging.CatUI, "target file size set to %s", state.convert.TargetFileSize) }) targetFileSizeSelect.SetSelected("Manual") updateTargetSizeOptions() - targetFileSizeEntry.SetText(state.convert.TargetFileSize) targetFileSizeEntry.OnChanged = func(val string) { - state.convert.TargetFileSize = val - if buildCommandPreview != nil { - buildCommandPreview() + updateTargetSizeState() + } + if state.convert.TargetFileSize != "" { + if num, unit, ok := parseSizeParts(state.convert.TargetFileSize); ok { + targetFileSizeEntry.SetText(num) + if unit != "" { + targetFileSizeUnitSelect.SetSelected(unit) + } + } else { + targetFileSizeEntry.SetText(state.convert.TargetFileSize) } } @@ -6043,7 +6119,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject { targetSizeContainer = container.NewVBox( widget.NewLabelWithStyle("Target File Size", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), targetFileSizeSelect, - targetFileSizeEntry, + targetSizeManualRow, ) encodingHint := widget.NewLabel("")