2026-03-28 11:29:42 +01:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"github.com/fatih/color"
|
|
|
|
|
"github.com/spf13/cobra"
|
2026-03-31 00:29:16 +02:00
|
|
|
tc "github.com/torrentclaw/go-client"
|
2026-03-30 13:06:07 +02:00
|
|
|
"github.com/torrentclaw/unarr/internal/config"
|
|
|
|
|
"github.com/torrentclaw/unarr/internal/sentry"
|
2026-05-21 14:46:10 +02:00
|
|
|
"github.com/torrentclaw/unarr/internal/upgrade"
|
2026-03-28 11:29:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
cfgFile string
|
|
|
|
|
apiKeyFlag string
|
|
|
|
|
jsonOut bool
|
|
|
|
|
noColor bool
|
|
|
|
|
rootCmd *cobra.Command
|
|
|
|
|
apiClient *tc.Client
|
|
|
|
|
appCfg config.Config
|
|
|
|
|
cfgLoaded bool
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
rootCmd = &cobra.Command{
|
|
|
|
|
Use: "unarr",
|
|
|
|
|
Short: "unarr — torrent search and management",
|
|
|
|
|
Long: `unarr is a powerful terminal tool for torrent search and management.
|
|
|
|
|
|
|
|
|
|
Search 30+ torrent sources, inspect torrent quality, discover popular content,
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
find streaming providers, and manage your media collection — all from your terminal.
|
|
|
|
|
|
|
|
|
|
Get started:
|
feat: replace setup with init wizard + interactive config menu
- `unarr init` (alias: `unarr setup`): streamlined 3-step wizard
(API key, download dir, daemon install). Removed method/name prompts
— auto-configured from defaults.
- `unarr config [category]`: interactive menu with 7 categories
(downloads, organization, notifications, device, region, connection,
advanced). Direct access via `unarr config downloads`, etc.
- Extract shared helpers (openBrowser, expandHome, isTerminal) to
helpers.go. Delete old setup.go and config.go.
- Update all "unarr setup" references to "unarr init" across daemon,
doctor, status, README, install scripts.
2026-03-29 12:09:03 +02:00
|
|
|
unarr init First-time configuration wizard
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
unarr search "breaking bad" Search for content
|
|
|
|
|
unarr start Start the download daemon
|
|
|
|
|
|
|
|
|
|
Documentation: https://torrentclaw.com/cli
|
2026-03-30 13:06:07 +02:00
|
|
|
Source: https://github.com/torrentclaw/unarr`,
|
2026-03-28 11:29:42 +01:00
|
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
|
|
|
if noColor || os.Getenv("NO_COLOR") != "" {
|
|
|
|
|
color.NoColor = true
|
|
|
|
|
}
|
2026-05-21 14:46:10 +02:00
|
|
|
// Self-updater fetches releases from the configured host (default
|
|
|
|
|
// torrentclaw.com), not GitHub — so mirrors / onion / staging /
|
|
|
|
|
// UNARR_API_URL all route updates correctly.
|
|
|
|
|
upgrade.SetBaseURL(loadConfig().Auth.APIURL)
|
2026-03-28 11:29:42 +01:00
|
|
|
},
|
|
|
|
|
SilenceUsage: true,
|
|
|
|
|
SilenceErrors: true,
|
|
|
|
|
}
|
|
|
|
|
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
// Command groups for organized help output
|
|
|
|
|
rootCmd.AddGroup(
|
|
|
|
|
&cobra.Group{ID: "start", Title: "Getting Started:"},
|
|
|
|
|
&cobra.Group{ID: "search", Title: "Search & Discovery:"},
|
|
|
|
|
&cobra.Group{ID: "download", Title: "Downloads & Streaming:"},
|
|
|
|
|
&cobra.Group{ID: "daemon", Title: "Daemon Management:"},
|
|
|
|
|
&cobra.Group{ID: "system", Title: "System & Diagnostics:"},
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-28 11:29:42 +01:00
|
|
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default ~/.config/unarr/config.toml)")
|
|
|
|
|
rootCmd.PersistentFlags().StringVar(&apiKeyFlag, "api-key", "", "API key (overrides config file and env)")
|
|
|
|
|
rootCmd.PersistentFlags().BoolVar(&jsonOut, "json", false, "output as JSON (for piping)")
|
|
|
|
|
rootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "disable colored output")
|
|
|
|
|
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
// Getting Started
|
feat: replace setup with init wizard + interactive config menu
- `unarr init` (alias: `unarr setup`): streamlined 3-step wizard
(API key, download dir, daemon install). Removed method/name prompts
— auto-configured from defaults.
- `unarr config [category]`: interactive menu with 7 categories
(downloads, organization, notifications, device, region, connection,
advanced). Direct access via `unarr config downloads`, etc.
- Extract shared helpers (openBrowser, expandHome, isTerminal) to
helpers.go. Delete old setup.go and config.go.
- Update all "unarr setup" references to "unarr init" across daemon,
doctor, status, README, install scripts.
2026-03-29 12:09:03 +02:00
|
|
|
initCmd := newInitCmd()
|
|
|
|
|
initCmd.GroupID = "start"
|
2026-04-01 12:20:51 +02:00
|
|
|
loginCmd := newLoginCmd()
|
|
|
|
|
loginCmd.GroupID = "start"
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
configCmd := newConfigCmd()
|
|
|
|
|
configCmd.GroupID = "start"
|
2026-03-29 16:54:32 +02:00
|
|
|
migrateCmd := newMigrateCmd()
|
|
|
|
|
migrateCmd.GroupID = "start"
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
|
|
|
|
|
// Search & Discovery
|
|
|
|
|
searchCmd := newSearchCmd()
|
|
|
|
|
searchCmd.GroupID = "search"
|
|
|
|
|
inspectCmd := newInspectCmd()
|
|
|
|
|
inspectCmd.GroupID = "search"
|
|
|
|
|
popularCmd := newPopularCmd()
|
|
|
|
|
popularCmd.GroupID = "search"
|
|
|
|
|
recentCmd := newRecentCmd()
|
|
|
|
|
recentCmd.GroupID = "search"
|
|
|
|
|
watchCmd := newWatchCmd()
|
|
|
|
|
watchCmd.GroupID = "search"
|
|
|
|
|
|
|
|
|
|
// Downloads & Streaming
|
|
|
|
|
downloadCmd := newDownloadCmd()
|
|
|
|
|
downloadCmd.GroupID = "download"
|
|
|
|
|
streamCmd := newStreamCmd()
|
|
|
|
|
streamCmd.GroupID = "download"
|
|
|
|
|
|
|
|
|
|
// Daemon Management
|
|
|
|
|
startCmd := newStartCmd()
|
|
|
|
|
startCmd.GroupID = "daemon"
|
|
|
|
|
stopCmd := newStopCmd()
|
|
|
|
|
stopCmd.GroupID = "daemon"
|
|
|
|
|
statusCmd := newStatusCmd()
|
|
|
|
|
statusCmd.GroupID = "daemon"
|
|
|
|
|
daemonCmd := newDaemonCmd()
|
|
|
|
|
daemonCmd.GroupID = "daemon"
|
2026-05-22 08:33:02 +02:00
|
|
|
vpnCmd := newVPNCmd()
|
|
|
|
|
vpnCmd.GroupID = "daemon"
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
|
|
|
|
|
// System & Diagnostics
|
|
|
|
|
statsCmd := newStatsCmd()
|
|
|
|
|
statsCmd.GroupID = "system"
|
|
|
|
|
doctorCmd := newDoctorCmd()
|
|
|
|
|
doctorCmd.GroupID = "system"
|
2026-05-08 15:57:02 +02:00
|
|
|
probeHWAccelCmd := newProbeHWAccelCmd()
|
|
|
|
|
probeHWAccelCmd.GroupID = "system"
|
2026-03-29 11:04:51 +02:00
|
|
|
cleanCmd := newCleanCmd()
|
|
|
|
|
cleanCmd.GroupID = "system"
|
2026-05-15 16:26:43 +02:00
|
|
|
mirrorsCmd := newMirrorsCmd()
|
|
|
|
|
mirrorsCmd.GroupID = "system"
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
selfUpdateCmd := newSelfUpdateCmd()
|
|
|
|
|
selfUpdateCmd.GroupID = "system"
|
|
|
|
|
versionCmd := newVersionCmd()
|
|
|
|
|
versionCmd.GroupID = "system"
|
|
|
|
|
completionCmd := newCompletionCmd()
|
|
|
|
|
completionCmd.GroupID = "system"
|
|
|
|
|
|
2026-03-29 16:54:32 +02:00
|
|
|
// Library
|
|
|
|
|
scanCmd := newScanCmd()
|
|
|
|
|
scanCmd.GroupID = "search"
|
|
|
|
|
|
2026-03-28 11:29:42 +01:00
|
|
|
rootCmd.AddCommand(
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
// Getting Started
|
feat: replace setup with init wizard + interactive config menu
- `unarr init` (alias: `unarr setup`): streamlined 3-step wizard
(API key, download dir, daemon install). Removed method/name prompts
— auto-configured from defaults.
- `unarr config [category]`: interactive menu with 7 categories
(downloads, organization, notifications, device, region, connection,
advanced). Direct access via `unarr config downloads`, etc.
- Extract shared helpers (openBrowser, expandHome, isTerminal) to
helpers.go. Delete old setup.go and config.go.
- Update all "unarr setup" references to "unarr init" across daemon,
doctor, status, README, install scripts.
2026-03-29 12:09:03 +02:00
|
|
|
initCmd,
|
2026-04-01 12:20:51 +02:00
|
|
|
loginCmd,
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
configCmd,
|
2026-03-29 16:54:32 +02:00
|
|
|
migrateCmd,
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
// Search & Discovery
|
|
|
|
|
searchCmd,
|
|
|
|
|
inspectCmd,
|
|
|
|
|
popularCmd,
|
|
|
|
|
recentCmd,
|
|
|
|
|
watchCmd,
|
|
|
|
|
// Downloads & Streaming
|
|
|
|
|
downloadCmd,
|
|
|
|
|
streamCmd,
|
|
|
|
|
// Daemon Management
|
|
|
|
|
startCmd,
|
|
|
|
|
stopCmd,
|
|
|
|
|
statusCmd,
|
|
|
|
|
daemonCmd,
|
2026-05-22 08:33:02 +02:00
|
|
|
vpnCmd,
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
// System & Diagnostics
|
|
|
|
|
statsCmd,
|
|
|
|
|
doctorCmd,
|
2026-05-08 15:57:02 +02:00
|
|
|
probeHWAccelCmd,
|
2026-03-29 11:04:51 +02:00
|
|
|
cleanCmd,
|
2026-05-15 16:26:43 +02:00
|
|
|
mirrorsCmd,
|
docs: improve CLI help, shell completion, and README
- Add command groups (Getting Started, Search, Downloads, Daemon, System)
- Add shell completion command (bash, zsh, fish, powershell)
- Add flag completions for --type, --quality, --sort, --lang, --genre,
--country, --method, --player
- Improve Long descriptions and Examples for all commands
- Split doctor disk check into platform-specific files (Unix/Windows)
- Validate infoHash length before truncating (prevent panic)
- Fix references to non-existent 'unarr daemon start' command
- Move stats command to System & Diagnostics group
- Rewrite README with complete documentation, correct config format
(toml not yaml), all commands, shell completion section
2026-03-28 21:36:27 +01:00
|
|
|
selfUpdateCmd,
|
|
|
|
|
versionCmd,
|
|
|
|
|
completionCmd,
|
2026-03-29 16:54:32 +02:00
|
|
|
// Library
|
|
|
|
|
scanCmd,
|
feat(cli): upgrade command, rich status, and version cache
- Replace `upgrade` stub with real command (alias for `self-update`)
- Also register `update` as alias: `unarr update` works too
- Rewrite `status` to show full config, disk usage, daemon state, and
update availability with colored sections
- Add version check cache (1h TTL) so `status` is instant on repeat runs
- Guard against division by zero on empty filesystems
- Guard against negative durations from clock skew
- Guard against stale PID via heartbeat recency check (2 min)
- Add comprehensive test coverage across agent, engine, upgrade, usenet,
arr, library, mediaserver, and UI packages
- Improve Makefile coverage target to exclude cmd/ glue code
- Fix stream handler resource cleanup and ffprobe error handling
2026-03-31 22:05:43 +02:00
|
|
|
// Alias: upgrade → self-update
|
|
|
|
|
newUpgradeCmd(),
|
2026-03-28 11:29:42 +01:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute runs the root command.
|
|
|
|
|
func Execute() {
|
|
|
|
|
if err := rootCmd.Execute(); err != nil {
|
2026-03-29 01:00:26 +01:00
|
|
|
// Report to Sentry with command context
|
|
|
|
|
command := ""
|
|
|
|
|
if cmd, _, cerr := rootCmd.Find(os.Args[1:]); cerr == nil && cmd != nil && cmd != rootCmd {
|
|
|
|
|
command = cmd.Name()
|
|
|
|
|
}
|
|
|
|
|
sentry.CaptureError(err, command)
|
|
|
|
|
sentry.Close() // Flush before os.Exit (defers don't run after os.Exit)
|
|
|
|
|
|
2026-03-28 11:29:42 +01:00
|
|
|
fmt.Fprintln(os.Stderr, color.RedString("Error: %s", err))
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// loadConfig loads config once (lazy initialization).
|
|
|
|
|
func loadConfig() config.Config {
|
|
|
|
|
if cfgLoaded {
|
|
|
|
|
return appCfg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
appCfg, err = config.Load(cfgFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintln(os.Stderr, color.YellowString("Warning: config load failed: %s", err))
|
|
|
|
|
appCfg = config.Default()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appCfg.ApplyEnvOverrides()
|
|
|
|
|
cfgLoaded = true
|
2026-03-29 01:00:26 +01:00
|
|
|
|
|
|
|
|
if appCfg.Agent.ID != "" {
|
|
|
|
|
sentry.SetUser(appCfg.Agent.ID)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 11:29:42 +01:00
|
|
|
return appCfg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getClient returns a configured API client, initializing it on first use.
|
|
|
|
|
func getClient() *tc.Client {
|
|
|
|
|
if apiClient != nil {
|
|
|
|
|
return apiClient
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg := loadConfig()
|
|
|
|
|
|
|
|
|
|
var opts []tc.Option
|
|
|
|
|
|
|
|
|
|
if cfg.Auth.APIURL != "" {
|
|
|
|
|
opts = append(opts, tc.WithBaseURL(cfg.Auth.APIURL))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apiKey := apiKeyFlag
|
|
|
|
|
if apiKey == "" {
|
|
|
|
|
apiKey = cfg.Auth.APIKey
|
|
|
|
|
}
|
|
|
|
|
if apiKey != "" {
|
|
|
|
|
opts = append(opts, tc.WithAPIKey(apiKey))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opts = append(opts, tc.WithUserAgent("unarr/"+Version))
|
|
|
|
|
|
|
|
|
|
apiClient = tc.NewClient(opts...)
|
|
|
|
|
return apiClient
|
|
|
|
|
}
|