Commit graph

189 commits

Author SHA1 Message Date
Deivid Soto
ef4f38d324 fix: resolve deadlock, data races and path traversal vulnerabilities
- task.go: fix deadlock in ToStatusUpdate() — calling Percent() (which
  RLocks) while already holding RLock caused deadlock when a writer was
  waiting; compute percent inline instead
- usenet.go: fix data race in Cancel() — tracker and taskDir were read
  without the mutex while Download() writes them under it; read all
  fields under the same lock
- upnp.go: fix UPnP Remove() blocking shutdown — run cleanup in goroutine
  with 10s deadline (removeNATPMP worst case is 3s dial + 5s deadline)
- daemon.go: add path traversal protection for stream requests — validate
  sr.FilePath is within configured directories before os.Stat; defends
  against compromised API server sending arbitrary paths
- client.go: add wakeClient without timeout for long-poll wake endpoint
  where context controls cancellation
- sync.go: trigger immediate sync when entering watching mode so stream
  requests are picked up without waiting for the next scheduled interval
2026-04-08 23:36:18 +02:00
Deivid Soto
78c16c295e test: add comprehensive test suite for engine, agent and cmd packages
- Refactor download.go and stream.go with downloadDeps/streamDeps structs
  for dependency injection, enabling unit testing without real I/O
- download_test.go: 15 tests — input validation, mock downloaders, method
  selection, cobra Args, deadlock detection
- stream_test.go: input validation, noOpen flag, engine error handling
- client_test.go: context cancellation, timeout, full Sync roundtrip,
  watch-progress and HTTP error unwrapping
- sync_test.go: TriggerSync on watching transition, adjustInterval
- torrent_test.go: TorrentDownloader lifecycle without network
- stream_server_test.go: HTTP server lifecycle, SetFile/ClearFile,
  concurrent requests, Shutdown releases port, content-type
- manager_integration_test.go: full pipeline — success, torrent→debrid
  fallback, all-fail, multi-concurrent, ForceStart, OnTaskDone,
  recent-finished drain, cancel mid-download, organize
- usenet_test.go: Cancel/Pause race regression test (run with -race)
- daemon_test.go: isAllowedStreamPath table tests
- CI: split coverage gate to engine+agent only (50% threshold); cmd
  coverage still reported but not gated (interactive UI commands)
- lefthook: add pre-push hook with go test -race -count=1 -timeout=120s
2026-04-08 23:36:00 +02:00
Deivid Soto
b14ab98580 chore(release): 0.6.0
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.6.0
- Update CHANGELOG.md
2026-04-08 18:57:36 +02:00
Deivid Soto
5d4a67c7a2 feat(sync): replace WS+DO transport with unified HTTP sync
Replace the WebSocket + Cloudflare Durable Object architecture with a
single POST /sync endpoint. The CLI now operates autonomously with local
state (tasks.json) and syncs bidirectionally via adaptive-interval HTTP
polling (3s watching, 60s idle).

- Remove transport_ws, transport_hybrid, transport_http (~2,600 lines)
- Add SyncClient with adaptive interval loop
- Add LocalState for CLI-side task persistence
- Add TaskStateFromUpdate() helper (DRY)
- Extract finalize() to deduplicate processTask/processTaskRetry
- Consolidate shortID() into agent.ShortID (was in 3 packages)
- Wire GetActiveCount so `unarr status` shows active tasks
- Remove poll_interval, heartbeat_interval, ws_url from config
- Simplify ProgressReporter (sync replaces direct HTTP reporting)
2026-04-08 18:50:59 +02:00
Deivid Soto
2398707cc1 fix(ws): add ping/pong keepalive and read deadline to detect zombie connections
Without a SetReadDeadline, a silently dead WebSocket (e.g. Cloudflare
dropping the connection without a close frame) would block readLoop
forever. The daemon would appear connected but never receive tasks,
and never fall back to HTTP polling.

- Send RFC 6455 pings every 30s (resets Cloudflare's idle timer)
- SetReadDeadline of 45s, refreshed on every pong and text message
- SetWriteDeadline of 10s on all writes to prevent blocked sends
- On timeout, readLoop emits "disconnected" → HybridTransport falls
  back to HTTP and starts WS reconnection loop
2026-04-08 00:06:19 +02:00
Deivid Soto
56a386f4e2 chore(release): 0.5.5
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.5
- Update CHANGELOG.md
2026-04-07 23:33:24 +02:00
Deivid Soto
4d7362a567 fix(daemon): cancel watch reporter on stream switch and re-notify ready
- Register WatchReporter cancel funcs in streamRegistry so they get
  cancelled when switching to a different stream (prevents goroutine leak)
- Re-notify streamReady when the server is already serving the requested
  task (handles duplicate stream requests from the web UI)
- Rewrite tests for byte-based tracking semantics, remove dead
  parseRangeStart tests
2026-04-07 23:30:53 +02:00
Deivid Soto
c612ebb2e4 feat(stream): report duration and position in watch progress
EstimatedProgress now returns video duration in seconds (from ffprobe).
WatchReporter sends Position and Duration fields when available, giving
the server precise playback time instead of just a percentage.
2026-04-07 23:29:00 +02:00
Deivid Soto
2dfe144df1 feat(stream): trackingReader with byte-based progress and rate limiting
Replace Range-header-based progress tracking with a trackingReader that
measures actual bytes read per connection. This gives accurate playback
position even for local/NAS files where VLC buffers aggressively.

- Token bucket rate limiter at 2x video bitrate (from ffprobe)
- CAS loops for lock-free atomic progress updates without regression
- probeMediaInfo extracts bitrate + duration via ffprobe (3s timeout)
- Defense-in-depth: only probe regular files, reject FIFOs/devices
- Remove dead parseRangeStart function
- Consistent [stream] log prefix
2026-04-07 23:28:53 +02:00
Deivid Soto
64734cad1f feat(agent): send stream port and IPs in register request
Include StreamPort, LanIP, and TailscaleIP in RegisterRequest so the
server knows the agent's stream endpoints from the moment it registers,
not just after the first heartbeat. Align HeartbeatRequest field order
with RegisterRequest for consistency.
2026-04-07 23:28:41 +02:00
Deivid Soto
bfa8ec5f11 chore(release): 0.5.4
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.4
- Update CHANGELOG.md
2026-04-07 19:18:41 +02:00
Deivid Soto
264be4e309 fix(stream): use platform-specific socket options for Windows cross-compilation 2026-04-07 19:18:13 +02:00
Deivid Soto
55fb74c814 chore(release): 0.5.3
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.3
- Update CHANGELOG.md
2026-04-07 19:08:49 +02:00
Deivid Soto
5994a30447 feat(stream): persistent stream server with file swapping 2026-04-07 19:08:37 +02:00
Deivid Soto
080fdf4d76 chore(release): 0.5.2
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.2
- Update CHANGELOG.md
2026-04-07 17:06:04 +02:00
Deivid Soto
eb8f5e8b1a feat(stream): report multi-network URLs for smart resolution 2026-04-07 17:05:52 +02:00
Deivid Soto
dc1a21d8f0 chore(release): 0.5.1
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.1
- Update CHANGELOG.md
2026-04-07 16:19:38 +02:00
Deivid Soto
d2edc08a1e fix(stream): prevent duplicate events from killing active stream server 2026-04-07 16:19:01 +02:00
Deivid Soto
a857661b27 fix(daemon): report failed status on stream request errors 2026-04-07 12:39:22 +02:00
Deivid Soto
a9179dc758 feat(daemon): add on-demand library scan via heartbeat and WebSocket 2026-04-07 11:36:42 +02:00
Deivid Soto
4cf07c411c fix(daemon): use correct systemd user target and isolate test cache 2026-04-06 18:49:44 +02:00
Deivid Soto
6f81a2f3ea fix(agent): add retry with backoff and WebSocket connect for daemon registration 2026-04-06 17:26:32 +02:00
Deivid Soto
8388220dae chore(release): 0.5.0
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Bump version to 0.5.0
- Update CHANGELOG.md
2026-04-06 10:16:57 +02:00
Deivid Soto
4d74b8cd8c test(mediainfo): add ffprobe download unit tests 2026-04-06 10:16:27 +02:00
Deivid Soto
eaf9d9d1c9 chore(release): add changelog generation and release automation 2026-04-06 10:16:01 +02:00
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
763e267bf8 chore(deps): bump Alpine 3.21→3.22, update CI actions and linter
Some checks failed
Release / release (push) Failing after 0s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
- Dockerfile: alpine 3.21 → 3.22 (fewer CVEs per Docker Scout)
- release.yml: actions/checkout v4→v6, setup-go v5→v6, setup-buildx v3→v4
- ci.yml: golangci-lint v2.11.3 → v2.11.4
- DOCKERHUB.md: update Alpine version reference
2026-03-31 11:39:45 +02:00
Deivid Soto
f15eefc0ff ci(docker): remove dockerhub-description sync step 2026-03-31 11:30:40 +02:00
Deivid Soto
e4f45332ca ci(docker): add Docker Hub description sync and DOCKERHUB.md
Some checks failed
Release / release (push) Failing after 1s
Release / docker (push) Has been skipped
Release / virustotal (push) Failing after 0s
2026-03-31 10:24:14 +02:00
Deivid Soto
af08073aa8 Merge remote-tracking branch 'origin/main' 2026-03-31 10:21:16 +02:00
Deivid Soto
3e60a2a056 fix(docker): upgrade alpine packages to patch CVE-2025-60876 and CVE-2026-27171 2026-03-31 10:20:30 +02:00
Deivid Soto
6d7c5d9174 Merge pull request #12 from torrentclaw/dependabot/github_actions/docker/metadata-action-6
ci(deps): bump docker/metadata-action from 5 to 6
2026-03-31 10:10:37 +02:00
Deivid Soto
b493456b92 Merge pull request #11 from torrentclaw/dependabot/github_actions/docker/setup-qemu-action-4
ci(deps): bump docker/setup-qemu-action from 3 to 4
2026-03-31 10:10:31 +02:00
Deivid Soto
125208e53b Merge pull request #10 from torrentclaw/dependabot/github_actions/docker/login-action-4
ci(deps): bump docker/login-action from 3 to 4
2026-03-31 10:10:25 +02:00
Deivid Soto
a184937287 Merge pull request #9 from torrentclaw/dependabot/github_actions/docker/build-push-action-7
ci(deps): bump docker/build-push-action from 6 to 7
2026-03-31 10:10:14 +02:00
Deivid Soto
b8bc4bcca5 Merge pull request #13 from torrentclaw/dependabot/github_actions/codecov/codecov-action-6
ci(deps): bump codecov/codecov-action from 5 to 6
2026-03-31 09:49:25 +02:00
dependabot[bot]
cf64d41109 ci(deps): bump codecov/codecov-action from 5 to 6
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 6.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 23:58:17 +00:00
dependabot[bot]
23d283587d ci(deps): bump docker/metadata-action from 5 to 6
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 23:58:14 +00:00
dependabot[bot]
085dfb0520 ci(deps): bump docker/setup-qemu-action from 3 to 4
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 23:58:11 +00:00
dependabot[bot]
a23d2ff336 ci(deps): bump docker/login-action from 3 to 4
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 23:58:08 +00:00
dependabot[bot]
94be50755e ci(deps): bump docker/build-push-action from 6 to 7
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 23:58:05 +00:00