main
Raw Download raw file
 1// Package cli implements the command-line interface.
 2package cli
 3
 4import (
 5	"fmt"
 6	"log/slog"
 7	"os"
 8
 9	"github.com/crash/upvs/internal/config"
10	"github.com/spf13/cobra"
11)
12
13var cfg = config.New()
14
15// Global flag variables for file-based password loading
16var passwordFile string
17
18// rootCmd is the base command.
19var rootCmd = &cobra.Command{
20	Use:   "upvs",
21	Short: "UniFi Protect Video Summarizer",
22	Long:  "Create ~10-minute timelapses from UniFi Protect camera recordings for a single day.",
23	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
24		// Load password from file if specified
25		if passwordFile != "" && cfg.Password == "" {
26			err := cfg.LoadPasswordFromFile(passwordFile)
27			if err != nil {
28				return err
29			}
30		}
31
32		// Set up logging
33		level := slog.LevelInfo
34		if cfg.Verbose {
35			level = slog.LevelDebug
36		}
37		handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})
38		slog.SetDefault(slog.New(handler))
39
40		return nil
41	},
42}
43
44func init() {
45	// Silence usage and errors on failure - we handle error printing in Execute()
46	rootCmd.SilenceUsage = true
47	rootCmd.SilenceErrors = true
48
49	// Connection flags
50	rootCmd.PersistentFlags().StringVar(&cfg.Host, "host", "", "UniFi Protect URL (env: UPVS_HOST)")
51	rootCmd.PersistentFlags().StringVar(&cfg.Username, "username", "", "Username (env: UPVS_USERNAME)")
52	rootCmd.PersistentFlags().StringVar(&cfg.Password, "password", "", "Password (env: UPVS_PASSWORD)")
53	rootCmd.PersistentFlags().StringVar(&passwordFile, "password-file", "", "Path to file containing password")
54	rootCmd.PersistentFlags().BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "Skip TLS verification")
55	rootCmd.PersistentFlags().BoolVar(&cfg.DirectAPI, "direct-api", false, "Use /api path (for direct NVR connection, not UniFi OS)")
56
57	// Target selection flags
58	rootCmd.PersistentFlags().StringVar(&cfg.Camera, "camera", "", "Camera ID or name")
59
60	// Output flags
61	rootCmd.PersistentFlags().StringVar(&cfg.OutDir, "out", "", "Output directory")
62	rootCmd.PersistentFlags().BoolVarP(&cfg.Verbose, "verbose", "v", false, "Enable debug logging")
63
64	// Bind environment variables
65	bindEnv("UPVS_HOST", &cfg.Host)
66	bindEnv("UPVS_USERNAME", &cfg.Username)
67	bindEnv("UPVS_PASSWORD", &cfg.Password)
68
69	// Add subcommands
70	rootCmd.AddCommand(scanCmd)
71	rootCmd.AddCommand(fetchCmd)
72	rootCmd.AddCommand(renderCmd)
73	rootCmd.AddCommand(runCmd)
74}
75
76// bindEnv sets a flag default from an environment variable if the flag is empty.
77func bindEnv(envVar string, target *string) {
78	if val := os.Getenv(envVar); val != "" && *target == "" {
79		*target = val
80	}
81}
82
83// Execute runs the root command.
84func Execute() {
85	err := rootCmd.Execute()
86	if err != nil {
87		fmt.Fprintln(os.Stderr, err)
88		os.Exit(1)
89	}
90}
91
92// GetConfig returns the global configuration.
93func GetConfig() *config.Config {
94	return cfg
95}