main
1package cli
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/crash/upvs/internal/config"
8 "github.com/crash/upvs/internal/output"
9 "github.com/crash/upvs/internal/pipeline"
10 "github.com/crash/upvs/internal/protect"
11 "github.com/spf13/cobra"
12)
13
14var dateStr string
15
16var scanCmd = &cobra.Command{
17 Use: "scan",
18 Short: "Enumerate events and create clip index",
19 Long: "Scan a day's events from UniFi Protect and write clip_index.json.",
20 RunE: runScan,
21}
22
23func init() {
24 scanCmd.Flags().StringVar(&dateStr, "date", "", "Target date YYYY-MM-DD (required)")
25 scanCmd.MarkFlagRequired("date")
26}
27
28func runScan(cmd *cobra.Command, args []string) error {
29 // Parse date
30 date, err := config.ParseDate(dateStr)
31 if err != nil {
32 return err
33 }
34 cfg.Date = date
35
36 // Validate required fields for scan
37 if cfg.Host == "" {
38 return config.ErrMissingHost
39 }
40 if cfg.Username == "" || cfg.Password == "" {
41 return config.ErrMissingCredentials
42 }
43 if cfg.Camera == "" {
44 return config.ErrMissingCamera
45 }
46 if cfg.OutDir == "" {
47 return config.ErrMissingOutDir
48 }
49
50 // Create layout and ensure directories
51 layout := output.NewLayout(cfg.OutDir, cfg.Camera, cfg.DateString())
52 err = layout.EnsureDirs()
53 if err != nil {
54 return err
55 }
56
57 // Create client
58 var opts []protect.ClientOption
59 if cfg.TLSInsecure {
60 opts = append(opts, protect.WithTLSInsecure())
61 }
62 if cfg.DirectAPI {
63 opts = append(opts, protect.WithDirectAPI())
64 }
65 client := protect.NewClient(cfg.Host, opts...)
66
67 // Login
68 ctx := context.Background()
69 err = client.Login(ctx, cfg.Username, cfg.Password)
70 if err != nil {
71 return fmt.Errorf("login: %w", err)
72 }
73
74 // Run scan
75 result, err := pipeline.Scan(ctx, client, layout, cfg.Camera, cfg.Date)
76 if err != nil {
77 return err
78 }
79
80 fmt.Printf("Scan complete: %d events for camera %s (%s)\n",
81 result.EventCount, result.Camera.Name, result.Camera.ID)
82 fmt.Printf("Clip index: %s\n", layout.ClipIndexPath())
83
84 return nil
85}