Commit graph

38 commits

Author SHA1 Message Date
Deivid Soto
aa6acbabc9 feat(stream): add NAT-PMP port mapping for remote downloads
Replace anacrolix/upnp with huin/goupnp + custom NAT-PMP (RFC 6886)
implementation. NAT-PMP is tried first (faster, more compatible with
TP-Link routers), with UPnP-IGD SOAP as fallback. Gateway detection
reads /proc/net/route for accuracy. Includes unit tests with mock
NAT-PMP server and permanent e2e tests (build tag manual).
2026-04-06 10:09:07 +02:00
Deivid Soto
819c727bf5 feat(organize): use server metadata for file organization and subtitle handling 2026-04-05 23:36:01 +02:00
Deivid Soto
48e4fb9f7b fix(lint): remove unused newStubCmd function
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
2026-04-01 12:29:05 +02:00
Deivid Soto
4d35e197f0 feat(cli): add login command and refactor shared helpers 2026-04-01 12:20:51 +02:00
Deivid Soto
0dafeaa70d feat(stream): report watch progress to API via HTTP Range tracking
Track the highest byte offset served by the stream server to estimate
playback progress (0-100%). A WatchReporter goroutine sends progress
to POST /api/internal/agent/watch-progress every 10s during streaming.

- Add maxByteOffset + totalFileSize to StreamServer for Range tracking
- Add FileSize() to fileProvider interface (all 3 providers)
- New WatchReporter: periodic progress reporter tied to daemon context
- New WatchProgressUpdate type with optional progress/position/duration
- Wire reporter into all 3 stream paths (task stream, disk stream, active download stream)
2026-04-01 12:16:45 +02:00
Deivid Soto
932312fc56 chore(cli): remove moreseed stub command 2026-03-31 23:12:07 +02:00
Deivid Soto
ab3b393c22 chore(cli): remove redundant stub commands (monitor, open, add, compare) 2026-03-31 23:03:08 +02:00
Deivid Soto
d0dbfc3d12 fix(ci): fix lint errors and pin CI to Go 1.25
- Run gofmt on all files
- Export SetupUPnP to fix unused lint error
- Remove Go 1.26 from CI matrix (only test with 1.25)
2026-03-31 22:15:12 +02:00
Deivid Soto
3e0f3a5a64 feat(cli): upgrade command, rich status, and version cache
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- 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
Deivid Soto
01d62ffa13 fix(progress): always report status transitions and poll for control signals 2026-03-31 16:55:50 +02:00
Deivid Soto
aed5f0475d fix(lint): use default:none to disable errcheck, fix all gofmt and exhaustive 2026-03-31 00:29:16 +02:00
Deivid Soto
4426219f35 fix(lint): disable errcheck, tune gosec/exclusions for codebase state 2026-03-31 00:21:17 +02:00
Deivid Soto
be6eef1195 fix(lint): configure linters for codebase maturity, fix gofmt and ineffassign 2026-03-31 00:17:19 +02:00
Deivid Soto
c0fd8d3818 fix(lint): exclude common fire-and-forget patterns from errcheck 2026-03-30 23:34:36 +02:00
Deivid Soto
104820f4fe fix(lint): resolve errcheck and bodyclose warnings for golangci-lint v2 2026-03-30 23:31:06 +02:00
Deivid Soto
efa4562acd refactor: migrate lint config to v2, remove daemon auto-upgrade, add trust badges
Some checks failed
Release / release (push) Failing after 1s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
2026-03-30 23:24:16 +02:00
Deivid Soto
b00e7fbf0e feat(init): add 60s countdown, skip key, and cancel detection to browser auth 2026-03-30 14:07:57 +02:00
Deivid Soto
16039a88a8 fix(build): unused variable in Windows process check 2026-03-30 13:11:55 +02:00
Deivid Soto
5a7449b9e6 chore: rename module from torrentclaw-cli to unarr
- Rename Go module path github.com/torrentclaw/torrentclaw-cli → github.com/torrentclaw/unarr
- Update all imports, ldflags, scripts, docs, and Docker config
- Add GitHub Actions release workflow (goreleaser on tag push)
2026-03-30 13:06:07 +02:00
Deivid Soto
61b44fe86f feat(stream): UPnP port forwarding for remote video playback
Some checks failed
Release / GoReleaser (push) Failing after 1s
- Add UPnP discovery and automatic port mapping (like Plex Remote Access)
- Stream server binds to 0.0.0.0 and reports public IP via UPnP
- Fallback chain: UPnP public IP → Tailscale IP → LAN IP
- Clean up port mapping on shutdown
- Bump version to 0.3.0-dev
2026-03-29 23:55:10 +02:00
Deivid Soto
d0f2abcd74 refactor: extract BuildSyncItems to library package, remove duplication 2026-03-29 20:44:33 +02:00
Deivid Soto
60176fadc2 fix: force-start tasks bypass HasCapacity check in dispatch loop 2026-03-29 20:33:51 +02:00
Deivid Soto
3badde606e fix: add panic recovery to auto-scan, cap DHT nodes at 200 2026-03-29 20:32:08 +02:00
Deivid Soto
c476bd865c feat(daemon): add auto-scan, force start, and stall timeout default
- Auto-scan: daemon scans library daily (configurable via config.toml)
  [library] auto_scan = true, scan_interval = "24h"
- Force start: tasks with forceStart=true bypass concurrency semaphore
  (like Transmission's Force Start — opens temporary extra slot)
- Stall timeout default: 30m instead of unlimited, prevents dead torrents
  from permanently blocking download slots
- ForceStart field in agent.Task for CLI/server communication
2026-03-29 20:22:15 +02:00
Deivid Soto
386c97f84a fix(torrent): expand tracker list, add DHT persistence and configurable timeouts
- Expand default trackers from 5 to 31 (synced with web tracker-list.ts)
- Add DHT node persistence between sessions (~/.local/share/unarr/dht-nodes.txt)
  Saves known nodes on shutdown, restores on startup for warm DHT bootstrap
- Make metadata_timeout and stall_timeout configurable in config.toml
  Default: 0 (unlimited, like qBittorrent) — users can set custom values
- Fix CleanTitle to handle web domains and format patterns (e.g. pctfenix.com)
2026-03-29 19:09:51 +02:00
Deivid Soto
20d4d34dfc feat(auth): browser-based CLI authentication (like Claude Code)
- New browser auth flow: CLI opens localhost server, browser redirects
  token back via callback — zero copy/paste needed
- Automatic fallback to manual API key entry if browser flow fails
- Server-side state validation with TTL to prevent phishing
- sync.Once guard on callback to prevent goroutine leaks
- Localhost-only redirect validation (regex + url.Parse)
- URL-escaped state parameter for safety
2026-03-29 17:53:18 +02:00
Deivid Soto
677a8fe083 feat: add migrate command, media server detection, and debrid auto-config
- Migration wizard from Sonarr/Radarr/Prowlarr (unarr migrate) [pre-beta]
  - Auto-detect instances via Docker, config files, port scan, Prowlarr
  - Import wanted list (monitored+missing movies/series)
  - Import download history and blocklist to avoid re-downloading
  - Extract debrid tokens from *arr download clients
  - Quality profile mapping to preferred_quality config
  - DISTINCT ON PostgreSQL query for optimal torrent selection
  - JSON export with --dry-run --json (text to stderr, JSON to stdout)
- Media server detection (Plex/Jellyfin/Emby) in unarr init
  - Detects library paths and offers them as download directory options
- Debrid auto-configuration in unarr init
  - Scans *arr instances for debrid tokens
  - Validates and saves via API if user confirms
- New preferred_quality setting in config (2160p/1080p/720p)
- Library scan command (unarr scan) with ffprobe metadata extraction
2026-03-29 16:54:32 +02:00
Deivid Soto
0b6c6849b1 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
Deivid Soto
35e5298f23 feat: add clean command to remove temp files, logs, and cached data
Adds `unarr clean` with interactive confirmation, --dry-run, --yes,
and --all flags. Safely skips recent usenet resume files (<7 days) to
preserve download progress. Includes platform-specific PID detection
(Unix signal 0 / Windows heartbeat heuristic), CleanableBytes callback
for future heartbeat reporting, and uses shared ui.FormatBytes.
2026-03-29 11:04:51 +02:00
Deivid Soto
3d6142a62e feat: add Sentry error reporting
Capture command errors and panics with Sentry SDK. DSN injected
at build time via ldflags (dev builds silent, releases report).
Opt-out: UNARR_NO_TELEMETRY=1.
2026-03-29 01:00:26 +01:00
Deivid Soto
6e07e82d51 fix: harden usenet/debrid downloaders from critico review
- Consolidate 3 maps (active, taskDirs, taskTrackers) into single
  activeDownload struct — eliminates out-of-sync state on mid-function panic
- Cancel() runs os.RemoveAll in background goroutine (non-blocking)
- Flush(): clear dirty before unlock to prevent concurrent flush race on
  same tmp file; remove fragile re-mark-on-error pattern
- Revert RWMutex → Mutex in ProgressTracker (negligible benefit under
  write-heavy workload, higher overhead)
- Remove file.Sync() from debrid and usenet downloaders (Close flushes
  kernel buffers; fsync blocks for seconds on large files)
- Pin golangci-lint to v2.1.6 in CI (was floating with `latest`)
- Fix CI matrix: Go 1.25+1.26 (was 1.24+1.25, but go.mod requires 1.25)
2026-03-28 22:11:04 +01:00
Deivid Soto
c9bcb96dab chore(deps): update all dependencies and GitHub Actions to latest
- Go deps: cobra 1.10.2, fatih/color 1.19, tablewriter 1.1.4,
  anacrolix/torrent 1.61, charmbracelet/huh 1.0, pion/webrtc 4.2.11
- GitHub Actions: checkout v6, setup-go v6, golangci-lint-action v9,
  codecov-action v5, ghaction-upx v4, goreleaser-action v7
- CI matrix: drop Go 1.22, test on 1.24 + 1.25
- Migrate tablewriter API from v0 to v1 (breaking change)
- Fix data race in WSTransport.readLoop (pass conn as parameter)
- Add file.Sync() before close in debrid and usenet downloaders
- Improve progress tracker: dedup MarkDone, re-mark dirty on flush error
2026-03-28 21:56:22 +01:00
Deivid Soto
719429b06e 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
Deivid Soto
197e33956a feat: improve daemon resilience, streaming, and usenet downloads
- Add daemon state persistence and stale resume file cleanup
- Add TriggerPoll for WebSocket resume actions
- Improve stream server with graceful shutdown and connection tracking
- Add desktop notifications for download completion
- Add media file organization with Movies/TV Shows detection
- Improve usenet downloader with progress tracking and resume support
- Add self-update package with GitHub release verification
- Downgrade tablewriter to v0.0.5 (v1.x API breaking change)
2026-03-28 21:36:12 +01:00
Deivid Soto
e332c0a6e4 feat(usenet): implement full NNTP download pipeline
Complete usenet download support for unarr CLI:
- NZB XML parser with password extraction from <head> meta
- yEnc decoder with CRC32 verification
- NNTP client with TLS, auth, and connection pool (up to 10 conns)
- Segment downloader with parallel workers and progress reporting
- Post-processing: par2 verify/repair, unrar/7z extraction with password support
- Agent client methods: SearchNzbs, DownloadNzb, GetUsenetCredentials
- UsenetDownloader implementing full Downloader interface
- Daemon wiring: UsenetDownloader passed to Manager

E2E tested: Oppenheimer 1080p (2.94 GB) downloaded via NNTP in 77.6s.
2026-03-28 21:12:12 +01:00
Deivid Soto
5f337eebd7 feat(agent): add WebSocket transport with HTTP fallback
Add Transport interface abstraction supporting WebSocket (via CF
Durable Objects) and HTTP (direct to origin) with automatic failover.

- Transport interface: Register, SendHeartbeat, SendProgress, Events()
- HTTPTransport: thin adapter over existing Client
- WSTransport: gorilla/websocket with auth handshake, readLoop, reconnect
- HybridTransport: tries WS first, falls back to HTTP, reconnects in bg
- Daemon refactored to always use Transport (no dual-path forks)
- ProgressReporter accepts StatusReporter interface
- deriveWSURL skips localhost/dev (returns "" → HTTP-only)
- API key passed in WS query param for connection auth
- Fixed: reconnectOnce race (mutex+bool), authDone double-close (sync.Once)
- Fixed: forwardWSEvents goroutine leak (select with stop signal)
- 20 transport tests + 2 E2E tests (full lifecycle, hybrid failover)
2026-03-28 18:55:29 +01:00
Deivid Soto
5e80911501 feat(debrid): add HTTPS downloader for debrid direct URLs
DebridDownloader receives directUrl from the server and downloads via
plain HTTPS with progress reporting, resume (Range), and pause/cancel.

- Add DirectURL, DirectFileName to agent Task and engine Task types
- Implement DebridDownloader: HTTPS download with progress, resume, cancel
- HTTP client with 30s ResponseHeaderTimeout
- Safe shortID helper to prevent slice panic on short IDs
- Validate 416 against Content-Range server size for resume integrity
- Register debridDl in daemon and one-shot download command
- Tests: available, download, resume, cancel, pause, fallback filename,
  expired URL (410), unauthorized (401), shutdown, task propagation
2026-03-28 18:09:34 +01:00
Deivid Soto
29cf0a0126 feat: initial commit — unarr CLI
Search, inspect, stream, and download torrents from the terminal.
Replaces the entire *arr stack with a single binary.
2026-03-28 11:29:42 +01:00