package main import ( "bufio" "fmt" "os" "strings" "gitea.dcglab.co.uk/steve/arrman/cmd" "gitea.dcglab.co.uk/steve/arrman/radarr" "gitea.dcglab.co.uk/steve/arrman/sonarr" ) // Version is set at build time via -ldflags. var Version = "dev" func main() { loadEnv() if len(os.Args) >= 2 && os.Args[1] == "version" { fmt.Println(Version) return } // Extract global --json flag from any position in args filteredArgs, jsonOutput := extractFlag(os.Args[1:], "--json") if len(filteredArgs) < 2 { usage() os.Exit(1) } domain := filteredArgs[0] action := filteredArgs[1] rest := filteredArgs[2:] tlsSkip := strings.EqualFold(os.Getenv("ARRMAN_TLS_SKIP_VERIFY"), "true") var err error switch domain { case "tv": sonarrURL := requireEnv("SONARR_URL") sonarrKey := requireEnv("SONARR_API_KEY") client := sonarr.NewClient(sonarrURL, sonarrKey, tlsSkip) err = runTV(client, action, rest, jsonOutput) case "film", "films": radarrURL := requireEnv("RADARR_URL") radarrKey := requireEnv("RADARR_API_KEY") client := radarr.NewClient(radarrURL, radarrKey, tlsSkip) err = runFilm(client, action, rest, jsonOutput) default: fmt.Fprintf(os.Stderr, "Unknown domain: %s\n", domain) usage() os.Exit(1) } if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } func extractFlag(args []string, flag string) ([]string, bool) { var remaining []string found := false for _, a := range args { if a == flag { found = true } else { remaining = append(remaining, a) } } return remaining, found } func runTV(client cmd.SonarrClient, action string, args []string, jsonOutput bool) error { switch action { case "list": rest, inclExt := extractFlag(args, "--include-external") term := strings.Join(rest, " ") return cmd.TVList(client, term, inclExt, jsonOutput) case "add": return cmd.TVAdd(client, args, jsonOutput) case "summary": term := strings.Join(args, " ") return cmd.TVSummary(client, term, jsonOutput) case "profiles": return cmd.TVProfiles(client, jsonOutput) case "rootfolders": return cmd.TVRootFolders(client, jsonOutput) default: return fmt.Errorf("unknown tv action: %s", action) } } func runFilm(client cmd.RadarrClient, action string, args []string, jsonOutput bool) error { switch action { case "list": rest, inclExt := extractFlag(args, "--include-external") term := strings.Join(rest, " ") return cmd.FilmList(client, term, inclExt, jsonOutput) case "add": return cmd.FilmAdd(client, args, jsonOutput) case "summary": term := strings.Join(args, " ") return cmd.FilmSummary(client, term, jsonOutput) case "profiles": return cmd.FilmProfiles(client, jsonOutput) case "rootfolders": return cmd.FilmRootFolders(client, jsonOutput) default: return fmt.Errorf("unknown film action: %s", action) } } func usage() { fmt.Fprintln(os.Stderr, `Usage: arrman [--json] [args...] Domains: tv, film, films Actions: list [title] [--include-external] List/search library (add flag to include external sources) add --profile --rootfolder Add new summary Show details profiles List quality profiles rootfolders List root folders Global flags: --json Output results as structured JSON`) } func loadEnv() { f, err := os.Open(".env") if err != nil { return } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { continue } key := strings.TrimSpace(parts[0]) val := strings.TrimSpace(parts[1]) if os.Getenv(key) == "" { os.Setenv(key, val) } } } func requireEnv(key string) string { val := os.Getenv(key) if val == "" { fmt.Fprintf(os.Stderr, "Error: %s is not set. Check your .env file.\n", key) os.Exit(1) } return val }