112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// Logger is the global logger
|
|
Logger *log.Logger
|
|
// LogFileHandle keeps the file handle to close it later
|
|
LogFileHandle *os.File
|
|
)
|
|
|
|
// SetupLogging configures logging to both console and file
|
|
func SetupLogging(logPath string) error {
|
|
// Close previous log file if open
|
|
if LogFileHandle != nil {
|
|
LogFileHandle.Close()
|
|
}
|
|
|
|
f, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
LogFileHandle = f
|
|
|
|
// MultiWriter to write to both stdout and file
|
|
mw := io.MultiWriter(os.Stdout, f)
|
|
Logger = log.New(mw, "", log.LstdFlags)
|
|
return nil
|
|
}
|
|
|
|
// LogInfo logs a message
|
|
func LogInfo(format string, v ...interface{}) {
|
|
if Logger != nil {
|
|
Logger.Printf(format, v...)
|
|
} else {
|
|
log.Printf(format, v...)
|
|
}
|
|
}
|
|
|
|
// RunCmd executes a command.
|
|
// If captureOutput is true, it returns the stdout as string.
|
|
// If captureOutput is false, it streams stdout/stderr to the console (and log file if possible).
|
|
func RunCmd(name string, args []string, captureOutput bool) (string, error) {
|
|
cmdStr := fmt.Sprintf("%s %s", name, strings.Join(args, " "))
|
|
LogInfo("Executing: %s", cmdStr)
|
|
|
|
cmd := exec.Command(name, args...)
|
|
|
|
if captureOutput {
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
return "", fmt.Errorf("command failed: %s\nstderr: %s", err, string(exitErr.Stderr))
|
|
}
|
|
return "", err
|
|
}
|
|
return string(output), nil
|
|
}
|
|
|
|
// Passthrough mode
|
|
// We want to stream to both stdout/stderr AND the log file if possible.
|
|
// However, for interactive tools or progress bars, simple MultiWriter might mess up formatting.
|
|
// For simplicity and safety with tools like av1an/ffmpeg showing progress bars,
|
|
// we will just pipe to Stdout/Stderr of the parent process.
|
|
// If we want to capture logs, we'd need a pipe that writes to file and stdout.
|
|
|
|
if LogFileHandle != nil {
|
|
cmd.Stdout = io.MultiWriter(os.Stdout, LogFileHandle)
|
|
cmd.Stderr = io.MultiWriter(os.Stderr, LogFileHandle)
|
|
} else {
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
}
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// CheckTools verifies that all required tools are in PATH
|
|
func CheckTools(tools []string) {
|
|
missing := []string{}
|
|
for _, tool := range tools {
|
|
_, err := exec.LookPath(tool)
|
|
if err != nil {
|
|
missing = append(missing, tool)
|
|
}
|
|
}
|
|
if len(missing) > 0 {
|
|
fmt.Printf("Error: The following required tools are missing from PATH:\n")
|
|
for _, m := range missing {
|
|
fmt.Printf(" - %s\n", m)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// GetTotalCores returns the number of logical CPUs
|
|
func GetTotalCores() int {
|
|
return runtime.NumCPU()
|
|
}
|