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 fetchCmd = &cobra.Command{
15 Use: "fetch",
16 Short: "Download video clips",
17 Long: "Download clips listed in clip_index.json with concurrent workers and retry support.",
18 RunE: runFetch,
19}
20
21func init() {
22 fetchCmd.Flags().StringVar(&dateStr, "date", "", "Target date YYYY-MM-DD (required)")
23 fetchCmd.Flags().IntVar(&cfg.Workers, "workers", config.DefaultWorkers, "Number of concurrent download workers")
24 fetchCmd.Flags().IntVar(&cfg.Retries, "retries", config.DefaultRetries, "Number of retry attempts per clip")
25 fetchCmd.MarkFlagRequired("date")
26}
27
28func runFetch(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
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
51 layout := output.NewLayout(cfg.OutDir, cfg.Camera, cfg.DateString())
52
53 // Create client
54 var opts []protect.ClientOption
55 if cfg.TLSInsecure {
56 opts = append(opts, protect.WithTLSInsecure())
57 }
58 if cfg.DirectAPI {
59 opts = append(opts, protect.WithDirectAPI())
60 }
61 client := protect.NewClient(cfg.Host, opts...)
62
63 // Login
64 ctx := context.Background()
65 err = client.Login(ctx, cfg.Username, cfg.Password)
66 if err != nil {
67 return fmt.Errorf("login: %w", err)
68 }
69
70 // Run fetch
71 result, err := pipeline.Fetch(ctx, client, layout, pipeline.FetchConfig{
72 Workers: cfg.Workers,
73 Retries: cfg.Retries,
74 })
75 if err != nil {
76 return err
77 }
78
79 fmt.Printf("Fetch complete: %d downloaded, %d skipped, %d failed\n",
80 result.Downloaded, result.Skipped, result.Failed)
81
82 if result.Failed > 0 {
83 fmt.Println("Failed clips:")
84 for _, e := range result.Errors {
85 fmt.Printf(" - %v\n", e)
86 }
87 }
88
89 return nil
90}