package log import ( "fmt" "os" "path/filepath" "runtime" "sync" "time" ) var ( logger *Logger once sync.Once logMutex sync.Mutex outputChan chan string verboseMode bool debugMode bool alwaysStderr bool ) type Logger struct { file *os.File filePath string enabled bool } type LogLevel int const ( LevelDebug LogLevel = iota LevelInfo LevelWarn LevelError ) func Init() { once.Do(func() { debugPrintf("=== Log Init Started ===") homeDir, err := os.UserHomeDir() if err != nil { debugPrintf("ERROR: os.UserHomeDir() failed: %v", err) fmt.Fprintf(os.Stderr, "[ERROR] Failed to get user home directory: %v\n", err) homeDir = "." } else { debugPrintf("User home directory: %s", homeDir) } var logDir string var logPath string // On Windows, use portable_config in detected MPV directory if runtime.GOOS == "windows" { logDir = filepath.Join(homeDir, "mpv", "portable_config") // Check if legacy path exists if _, err := os.Stat(logDir); os.IsNotExist(err) { // Try AppData path for fresh installs appData := os.Getenv("APPDATA") if appData != "" { appDataLogDir := filepath.Join(appData, "mpv", "portable_config") // Use AppData path if legacy doesn't exist logDir = appDataLogDir } } } else { logDir = filepath.Join(homeDir, ".config", "mpv") } debugPrintf("Attempting to create log directory: %s", logDir) // Try to create log directory if err := os.MkdirAll(logDir, 0755); err != nil { debugPrintf("ERROR: os.MkdirAll(%s) failed: %v", logDir, err) fmt.Fprintf(os.Stderr, "[ERROR] Failed to create log directory '%s': %v\n", logDir, err) // Try fallback to current directory logDir = "." debugPrintf("Falling back to current directory: %s", logDir) fmt.Fprintf(os.Stderr, "[INFO] Falling back to current directory for logs\n") if err := os.MkdirAll(logDir, 0755); err != nil { debugPrintf("ERROR: Fallback os.MkdirAll() also failed: %v", err) fmt.Fprintf(os.Stderr, "[ERROR] Failed to create fallback log directory: %v\n", err) return } } else { debugPrintf("Successfully created log directory") } logPath = filepath.Join(logDir, "mpv-manager.log") debugPrintf("Log file path: %s", logPath) file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { debugPrintf("ERROR: os.OpenFile(%s) failed: %v", logPath, err) fmt.Fprintf(os.Stderr, "[ERROR] Failed to create log file '%s': %v\n", logPath, err) return } else { debugPrintf("Successfully opened log file") } logger = &Logger{ file: file, filePath: logPath, enabled: true, } outputChan = make(chan string, 100) go func() { for msg := range outputChan { if logger.enabled && logger.file != nil { logger.file.WriteString(msg) } } }() Write("=== MPV.Rocks Installer Log Started ===") Write(fmt.Sprintf("Timestamp: %s", time.Now().Format(time.RFC3339))) debugPrintf("=== Log Init Complete ===") }) } func SetVerboseMode(enabled bool) { verboseMode = enabled if enabled { alwaysStderr = true } } func SetDebugMode(enabled bool) { debugMode = enabled if enabled { alwaysStderr = true } } func debugPrintf(format string, args ...interface{}) { if verboseMode || debugMode || alwaysStderr { msg := fmt.Sprintf(format, args...) fmt.Fprintf(os.Stderr, "[DEBUG] %s\n", msg) } } func Close() { if logger != nil { Write("=== MPV.Rocks Installer Log Ended ===") Flush() logMutex.Lock() if logger.file != nil { logger.file.Sync() logger.file.Close() } logger.enabled = false logMutex.Unlock() if outputChan != nil { close(outputChan) } } } func Flush() { if outputChan != nil { for len(outputChan) > 0 { time.Sleep(10 * time.Millisecond) } time.Sleep(50 * time.Millisecond) } } func Write(msg string) { if logger == nil || !logger.enabled { return } timestamp := time.Now().Format("2006-01-02 15:04:05") formatted := fmt.Sprintf("[%s] %s\n", timestamp, msg) select { case outputChan <- formatted: default: } } func Info(msg string) { Write(fmt.Sprintf("[INFO] %s", msg)) } func Debug(msg string) { Write(fmt.Sprintf("[DEBUG] %s", msg)) } func Warn(msg string) { Write(fmt.Sprintf("[WARN] %s", msg)) } func Error(msg string) { Write(fmt.Sprintf("[ERROR] %s", msg)) } func Command(cmd string) { Write(fmt.Sprintf("[COMMAND] %s", cmd)) } func Output(output string) { Write(fmt.Sprintf("[OUTPUT] %s", output)) } func ErrorOutput(output string) { Write(fmt.Sprintf("[ERROR-OUTPUT] %s", output)) } func Separator(msg string) { Write(fmt.Sprintf("--- %s ---", msg)) } func GetFilePath() string { if logger != nil { return logger.filePath } return "" }