Add simple bitrate/resolution/aspect controls and cache helper
This commit is contained in:
parent
a35049a6d8
commit
35a90bdcd6
|
|
@ -39,6 +39,11 @@ func formatBytes(b int64) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatBytes exposes human-readable bytes with binary units.
|
||||||
|
func FormatBytes(b int64) string {
|
||||||
|
return formatBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
// DeltaBytes renders size plus delta vs reference.
|
// DeltaBytes renders size plus delta vs reference.
|
||||||
func DeltaBytes(newBytes, refBytes int64) string {
|
func DeltaBytes(newBytes, refBytes int64) string {
|
||||||
if newBytes <= 0 {
|
if newBytes <= 0 {
|
||||||
|
|
@ -62,7 +67,7 @@ func DeltaBitrate(newBps, refBps int) string {
|
||||||
if newBps <= 0 {
|
if newBps <= 0 {
|
||||||
return "--"
|
return "--"
|
||||||
}
|
}
|
||||||
br := formatBitrate(newBps)
|
br := formatBitrateHuman(newBps)
|
||||||
if refBps <= 0 || refBps == newBps {
|
if refBps <= 0 || refBps == newBps {
|
||||||
return br
|
return br
|
||||||
}
|
}
|
||||||
|
|
@ -83,3 +88,14 @@ func formatPercent(val float64) string {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%.1f%%", val)
|
return fmt.Sprintf("%.1f%%", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatBitrateHuman(bps int) string {
|
||||||
|
if bps <= 0 {
|
||||||
|
return "--"
|
||||||
|
}
|
||||||
|
kbps := float64(bps) / 1000.0
|
||||||
|
if kbps >= 1000 {
|
||||||
|
return fmt.Sprintf("%.1f Mbps", kbps/1000.0)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.0f kbps", kbps)
|
||||||
|
}
|
||||||
|
|
|
||||||
139
main.go
139
main.go
|
|
@ -1334,10 +1334,6 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
|
||||||
cfg := job.Config
|
cfg := job.Config
|
||||||
inputPath := cfg["inputPath"].(string)
|
inputPath := cfg["inputPath"].(string)
|
||||||
outputPath := cfg["outputPath"].(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.
|
// If a direct conversion is running, wait until it finishes before starting queued jobs.
|
||||||
for s.convertBusy {
|
for s.convertBusy {
|
||||||
|
|
@ -1517,7 +1513,7 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
|
||||||
sourceWidth, _ := cfg["sourceWidth"].(int)
|
sourceWidth, _ := cfg["sourceWidth"].(int)
|
||||||
sourceHeight, _ := cfg["sourceHeight"].(int)
|
sourceHeight, _ := cfg["sourceHeight"].(int)
|
||||||
// Get source bitrate if present
|
// Get source bitrate if present
|
||||||
sourceBitrate = 0
|
sourceBitrate := 0
|
||||||
if v, ok := cfg["sourceBitrate"].(float64); ok {
|
if v, ok := cfg["sourceBitrate"].(float64); ok {
|
||||||
sourceBitrate = int(v)
|
sourceBitrate = int(v)
|
||||||
}
|
}
|
||||||
|
|
@ -1617,6 +1613,9 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
|
||||||
} else if bitrateMode == "CBR" {
|
} else if bitrateMode == "CBR" {
|
||||||
if videoBitrate, _ := cfg["videoBitrate"].(string); videoBitrate != "" {
|
if videoBitrate, _ := cfg["videoBitrate"].(string); videoBitrate != "" {
|
||||||
args = append(args, "-b:v", videoBitrate, "-minrate", videoBitrate, "-maxrate", videoBitrate, "-bufsize", videoBitrate)
|
args = append(args, "-b:v", videoBitrate, "-minrate", videoBitrate, "-maxrate", videoBitrate, "-bufsize", videoBitrate)
|
||||||
|
} else {
|
||||||
|
vb := defaultBitrate(videoCodec, sourceWidth, sourceBitrate)
|
||||||
|
args = append(args, "-b:v", vb, "-minrate", vb, "-maxrate", vb, "-bufsize", vb)
|
||||||
}
|
}
|
||||||
} else if bitrateMode == "VBR" {
|
} else if bitrateMode == "VBR" {
|
||||||
if videoBitrate, _ := cfg["videoBitrate"].(string); videoBitrate != "" {
|
if videoBitrate, _ := cfg["videoBitrate"].(string); videoBitrate != "" {
|
||||||
|
|
@ -2865,6 +2864,30 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}
|
}
|
||||||
bitratePresetSelect.SetSelected(state.convert.BitratePreset)
|
bitratePresetSelect.SetSelected(state.convert.BitratePreset)
|
||||||
|
|
||||||
|
// Simple bitrate selector (shares presets)
|
||||||
|
simpleBitrateSelect := widget.NewSelect(bitratePresetLabels, func(value string) {
|
||||||
|
state.convert.BitratePreset = value
|
||||||
|
if applyBitratePreset != nil {
|
||||||
|
applyBitratePreset(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
simpleBitrateSelect.SetSelected(state.convert.BitratePreset)
|
||||||
|
|
||||||
|
// Simple resolution selector (separate widget to avoid double-parent issues)
|
||||||
|
resolutionSelectSimple := widget.NewSelect([]string{"Source", "720p", "1080p", "1440p", "4K", "NTSC (720×480)", "PAL (720×576)"}, func(value string) {
|
||||||
|
state.convert.TargetResolution = value
|
||||||
|
logging.Debug(logging.CatUI, "target resolution set to %s (simple)", value)
|
||||||
|
})
|
||||||
|
resolutionSelectSimple.SetSelected(state.convert.TargetResolution)
|
||||||
|
|
||||||
|
// Simple aspect selector (separate widget)
|
||||||
|
targetAspectSelectSimple := widget.NewSelect(aspectTargets, func(value string) {
|
||||||
|
logging.Debug(logging.CatUI, "target aspect set to %s (simple)", value)
|
||||||
|
state.convert.OutputAspect = value
|
||||||
|
updateAspectBoxVisibility()
|
||||||
|
})
|
||||||
|
targetAspectSelectSimple.SetSelected(state.convert.OutputAspect)
|
||||||
|
|
||||||
// Target File Size with smart presets + manual entry
|
// Target File Size with smart presets + manual entry
|
||||||
targetFileSizeEntry = widget.NewEntry()
|
targetFileSizeEntry = widget.NewEntry()
|
||||||
targetFileSizeEntry.SetPlaceHolder("e.g., 25MB, 100MB, 8MB")
|
targetFileSizeEntry.SetPlaceHolder("e.g., 25MB, 100MB, 8MB")
|
||||||
|
|
@ -3028,11 +3051,14 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}
|
}
|
||||||
updateEncodingControls()
|
updateEncodingControls()
|
||||||
|
|
||||||
// Target Resolution
|
// Target Resolution (advanced)
|
||||||
resolutionSelect := widget.NewSelect([]string{"Source", "720p", "1080p", "1440p", "4K", "NTSC (720×480)", "PAL (720×576)"}, func(value string) {
|
resolutionSelect := widget.NewSelect([]string{"Source", "720p", "1080p", "1440p", "4K", "NTSC (720×480)", "PAL (720×576)"}, func(value string) {
|
||||||
state.convert.TargetResolution = value
|
state.convert.TargetResolution = value
|
||||||
logging.Debug(logging.CatUI, "target resolution set to %s", value)
|
logging.Debug(logging.CatUI, "target resolution set to %s", value)
|
||||||
})
|
})
|
||||||
|
if state.convert.TargetResolution == "" {
|
||||||
|
state.convert.TargetResolution = "Source"
|
||||||
|
}
|
||||||
resolutionSelect.SetSelected(state.convert.TargetResolution)
|
resolutionSelect.SetSelected(state.convert.TargetResolution)
|
||||||
|
|
||||||
// Frame Rate with hint
|
// Frame Rate with hint
|
||||||
|
|
@ -3217,7 +3243,14 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
widget.NewLabel("Choose slower for better compression, faster for speed"),
|
widget.NewLabel("Choose slower for better compression, faster for speed"),
|
||||||
widget.NewLabelWithStyle("Encoder Preset", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Encoder Preset", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
simplePresetSelect,
|
simplePresetSelect,
|
||||||
widget.NewLabel("Aspect ratio will match source video"),
|
widget.NewSeparator(),
|
||||||
|
widget.NewLabelWithStyle("Bitrate (simple presets)", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
|
simpleBitrateSelect,
|
||||||
|
widget.NewLabelWithStyle("Target Resolution", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
|
resolutionSelectSimple,
|
||||||
|
widget.NewLabelWithStyle("Target Aspect Ratio", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
|
targetAspectSelectSimple,
|
||||||
|
targetAspectHint,
|
||||||
layout.NewSpacer(),
|
layout.NewSpacer(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -3328,23 +3361,13 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
tabs.OnSelected = func(item *container.TabItem) {
|
tabs.OnSelected = func(item *container.TabItem) {
|
||||||
if item.Text == "Simple" {
|
if item.Text == "Simple" {
|
||||||
state.convert.Mode = "Simple"
|
state.convert.Mode = "Simple"
|
||||||
// Lock aspect ratio to Source in Simple mode
|
logging.Debug(logging.CatUI, "convert mode selected: Simple")
|
||||||
state.convert.OutputAspect = "Source"
|
|
||||||
targetAspectSelect.SetSelected("Source")
|
|
||||||
updateAspectBoxVisibility()
|
|
||||||
logging.Debug(logging.CatUI, "convert mode selected: Simple (aspect locked to Source)")
|
|
||||||
} else {
|
} else {
|
||||||
state.convert.Mode = "Advanced"
|
state.convert.Mode = "Advanced"
|
||||||
logging.Debug(logging.CatUI, "convert mode selected: Advanced")
|
logging.Debug(logging.CatUI, "convert mode selected: Advanced")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure Simple mode starts with Source aspect
|
|
||||||
if state.convert.Mode == "Simple" {
|
|
||||||
state.convert.OutputAspect = "Source"
|
|
||||||
targetAspectSelect.SetSelected("Source")
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsRect := canvas.NewRectangle(utils.MustHex("#13182B"))
|
optionsRect := canvas.NewRectangle(utils.MustHex("#13182B"))
|
||||||
optionsRect.CornerRadius = 8
|
optionsRect.CornerRadius = 8
|
||||||
optionsRect.StrokeColor = gridColor
|
optionsRect.StrokeColor = gridColor
|
||||||
|
|
@ -5307,6 +5330,7 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But
|
||||||
}
|
}
|
||||||
src := s.source
|
src := s.source
|
||||||
cfg := s.convert
|
cfg := s.convert
|
||||||
|
sourceBitrate := src.Bitrate
|
||||||
isDVD := cfg.SelectedFormat.Ext == ".mpg"
|
isDVD := cfg.SelectedFormat.Ext == ".mpg"
|
||||||
outDir := filepath.Dir(src.Path)
|
outDir := filepath.Dir(src.Path)
|
||||||
outName := cfg.OutputFile()
|
outName := cfg.OutputFile()
|
||||||
|
|
@ -6699,30 +6723,27 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
// File Info section
|
// File Info section
|
||||||
comparisonText.WriteString("━━━ FILE INFO ━━━\n")
|
comparisonText.WriteString("━━━ FILE INFO ━━━\n")
|
||||||
|
|
||||||
|
var file1SizeBytes int64
|
||||||
file1Size := getField(state.compareFile1, func(src *videoSource) string {
|
file1Size := getField(state.compareFile1, func(src *videoSource) string {
|
||||||
if fi, err := os.Stat(src.Path); err == nil {
|
if fi, err := os.Stat(src.Path); err == nil {
|
||||||
sizeMB := float64(fi.Size()) / (1024 * 1024)
|
file1SizeBytes = fi.Size()
|
||||||
if sizeMB >= 1024 {
|
return utils.FormatBytes(fi.Size())
|
||||||
return fmt.Sprintf("%.2f GB", sizeMB/1024)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%.2f MB", sizeMB)
|
|
||||||
}
|
}
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
})
|
})
|
||||||
file2Size := getField(state.compareFile2, func(src *videoSource) string {
|
file2Size := getField(state.compareFile2, func(src *videoSource) string {
|
||||||
if fi, err := os.Stat(src.Path); err == nil {
|
if fi, err := os.Stat(src.Path); err == nil {
|
||||||
sizeMB := float64(fi.Size()) / (1024 * 1024)
|
if file1SizeBytes > 0 {
|
||||||
if sizeMB >= 1024 {
|
return utils.DeltaBytes(fi.Size(), file1SizeBytes)
|
||||||
return fmt.Sprintf("%.2f GB", sizeMB/1024)
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%.2f MB", sizeMB)
|
return utils.FormatBytes(fi.Size())
|
||||||
}
|
}
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
})
|
})
|
||||||
|
|
||||||
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n", "File Size:", file1Size, file2Size))
|
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n", "File Size:", file1Size, file2Size))
|
||||||
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
||||||
"Format:",
|
"Format Family:",
|
||||||
getField(state.compareFile1, func(s *videoSource) string { return s.Format }),
|
getField(state.compareFile1, func(s *videoSource) string { return s.Format }),
|
||||||
getField(state.compareFile2, func(s *videoSource) string { return s.Format })))
|
getField(state.compareFile2, func(s *videoSource) string { return s.Format })))
|
||||||
|
|
||||||
|
|
@ -6747,7 +6768,12 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
||||||
"Bitrate:",
|
"Bitrate:",
|
||||||
getField(state.compareFile1, func(s *videoSource) string { return formatBitrate(s.Bitrate) }),
|
getField(state.compareFile1, func(s *videoSource) string { return formatBitrate(s.Bitrate) }),
|
||||||
getField(state.compareFile2, func(s *videoSource) string { return formatBitrate(s.Bitrate) })))
|
getField(state.compareFile2, func(s *videoSource) string {
|
||||||
|
if state.compareFile1 != nil {
|
||||||
|
return utils.DeltaBitrate(s.Bitrate, state.compareFile1.Bitrate)
|
||||||
|
}
|
||||||
|
return formatBitrate(s.Bitrate)
|
||||||
|
})))
|
||||||
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
comparisonText.WriteString(fmt.Sprintf("%-25s | %-20s | %s\n",
|
||||||
"Pixel Format:",
|
"Pixel Format:",
|
||||||
getField(state.compareFile1, func(s *videoSource) string { return s.PixelFormat }),
|
getField(state.compareFile1, func(s *videoSource) string { return s.PixelFormat }),
|
||||||
|
|
@ -6848,22 +6874,38 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
file2Info.Wrapping = fyne.TextWrapWord
|
file2Info.Wrapping = fyne.TextWrapWord
|
||||||
file2Info.TextStyle = fyne.TextStyle{} // non-selectable label
|
file2Info.TextStyle = fyne.TextStyle{} // non-selectable label
|
||||||
|
|
||||||
// Helper function to format metadata
|
// Helper function to format metadata (optionally comparing to a reference video)
|
||||||
formatMetadata := func(src *videoSource) string {
|
formatMetadata := func(src *videoSource, ref *videoSource) string {
|
||||||
fileSize := "Unknown"
|
var (
|
||||||
|
fileSize = "Unknown"
|
||||||
|
refSize int64 = 0
|
||||||
|
)
|
||||||
if fi, err := os.Stat(src.Path); err == nil {
|
if fi, err := os.Stat(src.Path); err == nil {
|
||||||
sizeMB := float64(fi.Size()) / (1024 * 1024)
|
if ref != nil {
|
||||||
if sizeMB >= 1024 {
|
if rfi, err := os.Stat(ref.Path); err == nil {
|
||||||
fileSize = fmt.Sprintf("%.2f GB", sizeMB/1024)
|
refSize = rfi.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if refSize > 0 {
|
||||||
|
fileSize = utils.DeltaBytes(fi.Size(), refSize)
|
||||||
} else {
|
} else {
|
||||||
fileSize = fmt.Sprintf("%.2f MB", sizeMB)
|
fileSize = utils.FormatBytes(fi.Size())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var bitrateStr string
|
|
||||||
if src.Bitrate > 0 {
|
var (
|
||||||
bitrateStr = formatBitrate(src.Bitrate)
|
|
||||||
} else {
|
|
||||||
bitrateStr = "--"
|
bitrateStr = "--"
|
||||||
|
refBitrate = 0
|
||||||
|
)
|
||||||
|
if ref != nil {
|
||||||
|
refBitrate = ref.Bitrate
|
||||||
|
}
|
||||||
|
if src.Bitrate > 0 {
|
||||||
|
if refBitrate > 0 {
|
||||||
|
bitrateStr = utils.DeltaBitrate(src.Bitrate, refBitrate)
|
||||||
|
} else {
|
||||||
|
bitrateStr = formatBitrate(src.Bitrate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
|
|
@ -6944,7 +6986,7 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
filename := filepath.Base(state.compareFile1.Path)
|
filename := filepath.Base(state.compareFile1.Path)
|
||||||
displayName := truncateFilename(filename, 35)
|
displayName := truncateFilename(filename, 35)
|
||||||
file1Label.SetText(fmt.Sprintf("File 1: %s", displayName))
|
file1Label.SetText(fmt.Sprintf("File 1: %s", displayName))
|
||||||
file1Info.SetText(formatMetadata(state.compareFile1))
|
file1Info.SetText(formatMetadata(state.compareFile1, state.compareFile2))
|
||||||
// Build video player with compact size for side-by-side
|
// Build video player with compact size for side-by-side
|
||||||
file1VideoContainer.Objects = []fyne.CanvasObject{
|
file1VideoContainer.Objects = []fyne.CanvasObject{
|
||||||
buildVideoPane(state, fyne.NewSize(320, 180), state.compareFile1, nil),
|
buildVideoPane(state, fyne.NewSize(320, 180), state.compareFile1, nil),
|
||||||
|
|
@ -6965,7 +7007,7 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
filename := filepath.Base(state.compareFile2.Path)
|
filename := filepath.Base(state.compareFile2.Path)
|
||||||
displayName := truncateFilename(filename, 35)
|
displayName := truncateFilename(filename, 35)
|
||||||
file2Label.SetText(fmt.Sprintf("File 2: %s", displayName))
|
file2Label.SetText(fmt.Sprintf("File 2: %s", displayName))
|
||||||
file2Info.SetText(formatMetadata(state.compareFile2))
|
file2Info.SetText(formatMetadata(state.compareFile2, state.compareFile1))
|
||||||
// Build video player with compact size for side-by-side
|
// Build video player with compact size for side-by-side
|
||||||
file2VideoContainer.Objects = []fyne.CanvasObject{
|
file2VideoContainer.Objects = []fyne.CanvasObject{
|
||||||
buildVideoPane(state, fyne.NewSize(320, 180), state.compareFile2, nil),
|
buildVideoPane(state, fyne.NewSize(320, 180), state.compareFile2, nil),
|
||||||
|
|
@ -7030,7 +7072,7 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
if state.compareFile1 == nil {
|
if state.compareFile1 == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
metadata := formatMetadata(state.compareFile1)
|
metadata := formatMetadata(state.compareFile1, state.compareFile2)
|
||||||
state.window.Clipboard().SetContent(metadata)
|
state.window.Clipboard().SetContent(metadata)
|
||||||
dialog.ShowInformation("Copied", "Metadata copied to clipboard", state.window)
|
dialog.ShowInformation("Copied", "Metadata copied to clipboard", state.window)
|
||||||
})
|
})
|
||||||
|
|
@ -7047,7 +7089,7 @@ func buildCompareView(state *appState) fyne.CanvasObject {
|
||||||
if state.compareFile2 == nil {
|
if state.compareFile2 == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
metadata := formatMetadata(state.compareFile2)
|
metadata := formatMetadata(state.compareFile2, state.compareFile1)
|
||||||
state.window.Clipboard().SetContent(metadata)
|
state.window.Clipboard().SetContent(metadata)
|
||||||
dialog.ShowInformation("Copied", "Metadata copied to clipboard", state.window)
|
dialog.ShowInformation("Copied", "Metadata copied to clipboard", state.window)
|
||||||
})
|
})
|
||||||
|
|
@ -7159,19 +7201,14 @@ func buildInspectView(state *appState) fyne.CanvasObject {
|
||||||
formatMetadata := func(src *videoSource) string {
|
formatMetadata := func(src *videoSource) string {
|
||||||
fileSize := "Unknown"
|
fileSize := "Unknown"
|
||||||
if fi, err := os.Stat(src.Path); err == nil {
|
if fi, err := os.Stat(src.Path); err == nil {
|
||||||
sizeMB := float64(fi.Size()) / (1024 * 1024)
|
fileSize = utils.FormatBytes(fi.Size())
|
||||||
if sizeMB >= 1024 {
|
|
||||||
fileSize = fmt.Sprintf("%.2f GB", sizeMB/1024)
|
|
||||||
} else {
|
|
||||||
fileSize = fmt.Sprintf("%.2f MB", sizeMB)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"━━━ FILE INFO ━━━\n"+
|
"━━━ FILE INFO ━━━\n"+
|
||||||
"Path: %s\n"+
|
"Path: %s\n"+
|
||||||
"File Size: %s\n"+
|
"File Size: %s\n"+
|
||||||
"Format: %s\n"+
|
"Format Family: %s\n"+
|
||||||
"\n━━━ VIDEO ━━━\n"+
|
"\n━━━ VIDEO ━━━\n"+
|
||||||
"Codec: %s\n"+
|
"Codec: %s\n"+
|
||||||
"Resolution: %dx%d\n"+
|
"Resolution: %dx%d\n"+
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ set -e
|
||||||
|
|
||||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
BUILD_OUTPUT="$PROJECT_ROOT/VideoTools"
|
BUILD_OUTPUT="$PROJECT_ROOT/VideoTools"
|
||||||
# Extract app version from main.go
|
# Extract app version from main.go (avoid grep warnings on Git Bash)
|
||||||
APP_VERSION="$(grep -E 'appVersion\s*=\s*\"' "$PROJECT_ROOT/main.go" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')"
|
APP_VERSION="$(grep -m1 'appVersion' "$PROJECT_ROOT/main.go" | sed -E 's/.*\"([^\"]+)\".*/\1/')"
|
||||||
[ -z "$APP_VERSION" ] && APP_VERSION="(version unknown)"
|
[ -z "$APP_VERSION" ] && APP_VERSION="(version unknown)"
|
||||||
|
|
||||||
echo "════════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
|
@ -64,5 +64,10 @@ if go build -o "$BUILD_OUTPUT" .; then
|
||||||
else
|
else
|
||||||
echo "❌ Build failed! (VideoTools $APP_VERSION)"
|
echo "❌ Build failed! (VideoTools $APP_VERSION)"
|
||||||
echo "Diagnostics: version=$APP_VERSION os=$(uname -s) arch=$(uname -m) go=$(go version | awk '{print $3}')"
|
echo "Diagnostics: version=$APP_VERSION os=$(uname -s) arch=$(uname -m) go=$(go version | awk '{print $3}')"
|
||||||
|
echo ""
|
||||||
|
echo "Help: check the Go error messages above."
|
||||||
|
echo " - Undefined symbol/identifier: usually a missing variable or typo in source; see the referenced file:line."
|
||||||
|
echo " - \"C compiler not found\": install a C toolchain (e.g., build-essential on Ubuntu, Xcode CLT on macOS)."
|
||||||
|
echo " - Cache permission denied: run 'rm -rf ~/.cache/go-build' or 'chown -R $USER ~/.cache/go-build'."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ set -e
|
||||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
# Extract app version from main.go
|
# Extract app version from main.go (avoid grep warnings on Git Bash)
|
||||||
APP_VERSION="$(grep -E 'appVersion[[:space:]]*=[[:space:]]*\"' "$PROJECT_ROOT/main.go" | head -1 | sed -E 's/.*\"([^\"]+)\".*/\1/')"
|
APP_VERSION="$(grep -m1 'appVersion' "$PROJECT_ROOT/main.go" | sed -E 's/.*\"([^\"]+)\".*/\1/')"
|
||||||
[ -z "$APP_VERSION" ] && APP_VERSION="(version unknown)"
|
[ -z "$APP_VERSION" ] && APP_VERSION="(version unknown)"
|
||||||
|
|
||||||
echo "════════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
|
@ -97,6 +97,11 @@ case "$OS" in
|
||||||
else
|
else
|
||||||
echo "❌ Build failed! (VideoTools $APP_VERSION)"
|
echo "❌ Build failed! (VideoTools $APP_VERSION)"
|
||||||
diagnostics
|
diagnostics
|
||||||
|
echo ""
|
||||||
|
echo "Help: check the Go error messages above."
|
||||||
|
echo " - Undefined symbol/identifier: usually a missing variable or typo in source; see the referenced file:line."
|
||||||
|
echo " - \"C compiler not found\": install MinGW-w64 or MSYS2 toolchain so gcc is in PATH."
|
||||||
|
echo " - Cache permission denied: delete or chown the Go build cache (e.g., %LOCALAPPDATA%\\go-build on Windows)."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
39
scripts/clear-go-cache.sh
Normal file
39
scripts/clear-go-cache.sh
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Convenience script to clear Go build/module caches.
|
||||||
|
# Safe to run on Linux/macOS and Windows Git Bash.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
echo " VideoTools Go Cache Cleaner"
|
||||||
|
echo "════════════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if ! command -v go >/dev/null 2>&1; then
|
||||||
|
echo "⚠️ Go is not installed or not in PATH; skipping go clean."
|
||||||
|
else
|
||||||
|
echo "🧹 Running: go clean -cache -modcache -testcache"
|
||||||
|
go clean -cache -modcache -testcache || true
|
||||||
|
echo "✓ Go clean complete"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OS="$(uname -s)"
|
||||||
|
case "$OS" in
|
||||||
|
CYGWIN*|MINGW*|MSYS*)
|
||||||
|
# Windows paths under Git Bash
|
||||||
|
CACHE_DIR="${LOCALAPPDATA:-$APPDATA}/go-build"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
CACHE_DIR="${GOCACHE:-$HOME/.cache/go-build}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "$CACHE_DIR" ] && [ -d "$CACHE_DIR" ]; then
|
||||||
|
echo "🗑️ Removing build cache dir: $CACHE_DIR"
|
||||||
|
rm -rf "$CACHE_DIR" || sudo rm -rf "$CACHE_DIR" || true
|
||||||
|
else
|
||||||
|
echo "ℹ️ No cache directory found at $CACHE_DIR (nothing to remove)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Done. Re-run ./scripts/build.sh to rebuild VideoTools."
|
||||||
Loading…
Reference in New Issue
Block a user