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)
This commit is contained in:
parent
9cc806d11f
commit
5a7449b9e6
58 changed files with 166 additions and 141 deletions
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Questions & Discussion
|
- name: Questions & Discussion
|
||||||
url: https://github.com/torrentclaw/torrentclaw-cli/discussions
|
url: https://github.com/torrentclaw/unarr/discussions
|
||||||
about: Ask questions or start a discussion
|
about: Ask questions or start a discussion
|
||||||
|
|
|
||||||
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
|
|
@ -10,30 +10,20 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: GoReleaser
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Checkout go-client (local replace dependency)
|
- uses: actions/setup-go@v5
|
||||||
run: git clone --depth 1 https://github.com/torrentclaw/go-client "$GITHUB_WORKSPACE/../go-client"
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
with:
|
||||||
go-version: "1.25"
|
go-version-file: go.mod
|
||||||
|
|
||||||
- name: Install UPX
|
- uses: goreleaser/goreleaser-action@v6
|
||||||
uses: crazy-max/ghaction-upx@v4
|
|
||||||
with:
|
with:
|
||||||
install-only: true
|
version: "~> v2"
|
||||||
|
|
||||||
- name: Run GoReleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v7
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
args: release --clean
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ builds:
|
||||||
- arm64
|
- arm64
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w
|
- -s -w
|
||||||
- -X github.com/torrentclaw/torrentclaw-cli/internal/cmd.Version={{.Version}}
|
- -X github.com/torrentclaw/unarr/internal/cmd.Version={{.Version}}
|
||||||
- -X github.com/torrentclaw/torrentclaw-cli/internal/sentry.dsn={{ .Env.SENTRY_DSN }}
|
- -X github.com/torrentclaw/unarr/internal/sentry.dsn={{ .Env.SENTRY_DSN }}
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
- format: tar.gz
|
- format: tar.gz
|
||||||
|
|
@ -42,7 +42,7 @@ brews:
|
||||||
owner: torrentclaw
|
owner: torrentclaw
|
||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
name: unarr
|
name: unarr
|
||||||
homepage: https://github.com/torrentclaw/torrentclaw-cli
|
homepage: https://github.com/torrentclaw/unarr
|
||||||
description: "unarr — replaces the entire *arr stack"
|
description: "unarr — replaces the entire *arr stack"
|
||||||
license: MIT
|
license: MIT
|
||||||
install: |
|
install: |
|
||||||
|
|
|
||||||
|
|
@ -53,5 +53,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- CI pipeline (test, build, lint, vet)
|
- CI pipeline (test, build, lint, vet)
|
||||||
- Lefthook git hooks (gofmt, go vet, conventional commits)
|
- Lefthook git hooks (gofmt, go vet, conventional commits)
|
||||||
|
|
||||||
[Unreleased]: https://github.com/torrentclaw/torrentclaw-cli/compare/v0.1.0...HEAD
|
[Unreleased]: https://github.com/torrentclaw/unarr/compare/v0.1.0...HEAD
|
||||||
[0.1.0]: https://github.com/torrentclaw/torrentclaw-cli/releases/tag/v0.1.0
|
[0.1.0]: https://github.com/torrentclaw/unarr/releases/tag/v0.1.0
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,15 @@ Thank you for your interest in contributing! This guide will help you get starte
|
||||||
1. **Fork** the repository on GitHub
|
1. **Fork** the repository on GitHub
|
||||||
2. **Clone** your fork locally:
|
2. **Clone** your fork locally:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/YOUR-USERNAME/torrentclaw-cli.git
|
git clone https://github.com/YOUR-USERNAME/unarr.git
|
||||||
cd torrentclaw-cli
|
cd unarr
|
||||||
```
|
```
|
||||||
3. **Set up the Go client** (local dependency):
|
3. **Set up the Go client** (local dependency):
|
||||||
```bash
|
```bash
|
||||||
# Clone the go-client next to the CLI
|
# Clone the go-client next to the CLI
|
||||||
cd ..
|
cd ..
|
||||||
git clone https://github.com/torrentclaw/go-client.git
|
git clone https://github.com/torrentclaw/go-client.git
|
||||||
cd torrentclaw-cli
|
cd unarr
|
||||||
```
|
```
|
||||||
4. **Create a branch** for your change:
|
4. **Create a branch** for your change:
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -23,7 +23,7 @@ Thank you for your interest in contributing! This guide will help you get starte
|
||||||
```
|
```
|
||||||
5. **Make your changes**, write tests, and ensure everything passes
|
5. **Make your changes**, write tests, and ensure everything passes
|
||||||
6. **Commit** with a clear message (see [Commit Messages](#commit-messages))
|
6. **Commit** with a clear message (see [Commit Messages](#commit-messages))
|
||||||
7. **Push** to your fork and [open a Pull Request](https://github.com/torrentclaw/torrentclaw-cli/compare)
|
7. **Push** to your fork and [open a Pull Request](https://github.com/torrentclaw/unarr/compare)
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ make install-hooks # Install lefthook git hooks
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
torrentclaw-cli/
|
unarr/
|
||||||
├── cmd/unarr/ # Entry point
|
├── cmd/unarr/ # Entry point
|
||||||
│ └── main.go
|
│ └── main.go
|
||||||
├── internal/
|
├── internal/
|
||||||
|
|
@ -176,7 +176,7 @@ chore: update CI matrix to Go 1.24
|
||||||
|
|
||||||
## Reporting Bugs
|
## Reporting Bugs
|
||||||
|
|
||||||
[Open an issue](https://github.com/torrentclaw/torrentclaw-cli/issues/new?labels=bug) with:
|
[Open an issue](https://github.com/torrentclaw/unarr/issues/new?labels=bug) with:
|
||||||
|
|
||||||
- **Description** — what went wrong
|
- **Description** — what went wrong
|
||||||
- **Steps to reproduce** — minimal commands to trigger the bug
|
- **Steps to reproduce** — minimal commands to trigger the bug
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# ---- Build stage ----
|
# ---- Build stage ----
|
||||||
# Build context must be the parent directory containing both torrentclaw-cli/
|
# Build context must be the parent directory containing both unarr/
|
||||||
# and go-client/. Use: docker build -f torrentclaw-cli/Dockerfile .
|
# and go-client/. Use: docker build -f unarr/Dockerfile .
|
||||||
# Or use the provided docker-build.sh script.
|
# Or use the provided docker-build.sh script.
|
||||||
FROM golang:1.24-alpine AS builder
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
|
|
@ -12,12 +12,12 @@ COPY go-client/ /deps/go-client/
|
||||||
|
|
||||||
# Copy go.mod/go.sum first for layer caching
|
# Copy go.mod/go.sum first for layer caching
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY torrentclaw-cli/go.mod torrentclaw-cli/go.sum ./
|
COPY unarr/go.mod unarr/go.sum ./
|
||||||
RUN go mod edit -replace github.com/torrentclaw/go-client=/deps/go-client
|
RUN go mod edit -replace github.com/torrentclaw/go-client=/deps/go-client
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
# Copy source (changes here won't invalidate mod cache)
|
# Copy source (changes here won't invalidate mod cache)
|
||||||
COPY torrentclaw-cli/ .
|
COPY unarr/ .
|
||||||
RUN go mod edit -replace github.com/torrentclaw/go-client=/deps/go-client
|
RUN go mod edit -replace github.com/torrentclaw/go-client=/deps/go-client
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /unarr ./cmd/unarr/
|
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /unarr ./cmd/unarr/
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
BINARY = unarr
|
BINARY = unarr
|
||||||
SENTRY_DSN ?=
|
SENTRY_DSN ?=
|
||||||
LDFLAGS = -s -w -X github.com/torrentclaw/torrentclaw-cli/internal/sentry.dsn=$(SENTRY_DSN)
|
LDFLAGS = -s -w -X github.com/torrentclaw/unarr/internal/sentry.dsn=$(SENTRY_DSN)
|
||||||
|
|
||||||
all: fmt vet lint test build
|
all: fmt vet lint test build
|
||||||
|
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -1,9 +1,9 @@
|
||||||
# unarr
|
# unarr
|
||||||
|
|
||||||
[](https://github.com/torrentclaw/torrentclaw-cli/actions/workflows/ci.yml)
|
[](https://github.com/torrentclaw/unarr/actions/workflows/ci.yml)
|
||||||
[](https://goreportcard.com/report/github.com/torrentclaw/torrentclaw-cli)
|
[](https://goreportcard.com/report/github.com/torrentclaw/unarr)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](go.mod)
|
[](go.mod)
|
||||||
|
|
||||||
Powerful terminal tool for torrent search and management.
|
Powerful terminal tool for torrent search and management.
|
||||||
|
|
||||||
|
|
@ -35,18 +35,18 @@ brew install torrentclaw/tap/unarr
|
||||||
### Go install
|
### Go install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go install github.com/torrentclaw/torrentclaw-cli/cmd/unarr@latest
|
go install github.com/torrentclaw/unarr/cmd/unarr@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### GitHub Releases
|
### GitHub Releases
|
||||||
|
|
||||||
Download prebuilt binaries for Linux, macOS, and Windows from [GitHub Releases](https://github.com/torrentclaw/torrentclaw-cli/releases).
|
Download prebuilt binaries for Linux, macOS, and Windows from [GitHub Releases](https://github.com/torrentclaw/unarr/releases).
|
||||||
|
|
||||||
### Build from source
|
### Build from source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/torrentclaw/torrentclaw-cli.git
|
git clone https://github.com/torrentclaw/unarr.git
|
||||||
cd torrentclaw-cli
|
cd unarr
|
||||||
make build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Only the latest release receives security updates.
|
||||||
|
|
||||||
Instead, report them via **GitHub Security Advisories**:
|
Instead, report them via **GitHub Security Advisories**:
|
||||||
|
|
||||||
1. Go to [Security Advisories](https://github.com/torrentclaw/torrentclaw-cli/security/advisories)
|
1. Go to [Security Advisories](https://github.com/torrentclaw/unarr/security/advisories)
|
||||||
2. Click **"Report a vulnerability"**
|
2. Click **"Report a vulnerability"**
|
||||||
3. Fill in the details
|
3. Fill in the details
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/cmd"
|
"github.com/torrentclaw/unarr/internal/cmd"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/sentry"
|
"github.com/torrentclaw/unarr/internal/sentry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Build the unarr Docker image.
|
# Build the unarr Docker image.
|
||||||
# Must be run from the torrentclaw-cli directory (or its parent).
|
# Must be run from the unarr directory (or its parent).
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
# Build from parent dir so both torrentclaw-cli/ and go-client/ are in context
|
# Build from parent dir so both unarr/ and go-client/ are in context
|
||||||
docker build \
|
docker build \
|
||||||
-f "$SCRIPT_DIR/Dockerfile" \
|
-f "$SCRIPT_DIR/Dockerfile" \
|
||||||
-t torrentclaw/unarr:latest \
|
-t torrentclaw/unarr:latest \
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ services:
|
||||||
unarr:
|
unarr:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: torrentclaw-cli/Dockerfile
|
dockerfile: unarr/Dockerfile
|
||||||
image: torrentclaw/unarr:latest
|
image: torrentclaw/unarr:latest
|
||||||
container_name: unarr
|
container_name: unarr
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
|
||||||
6
go.mod
6
go.mod
|
|
@ -1,11 +1,13 @@
|
||||||
module github.com/torrentclaw/torrentclaw-cli
|
module github.com/torrentclaw/unarr
|
||||||
|
|
||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.6.0
|
github.com/BurntSushi/toml v1.6.0
|
||||||
|
github.com/anacrolix/dht/v2 v2.23.0
|
||||||
github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb
|
github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb
|
||||||
github.com/anacrolix/torrent v1.61.0
|
github.com/anacrolix/torrent v1.61.0
|
||||||
|
github.com/anacrolix/upnp v0.1.4
|
||||||
github.com/charmbracelet/huh v1.0.0
|
github.com/charmbracelet/huh v1.0.0
|
||||||
github.com/fatih/color v1.19.0
|
github.com/fatih/color v1.19.0
|
||||||
github.com/getsentry/sentry-go v0.44.1
|
github.com/getsentry/sentry-go v0.44.1
|
||||||
|
|
@ -22,7 +24,6 @@ require (
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
||||||
github.com/anacrolix/btree v0.1.1 // indirect
|
github.com/anacrolix/btree v0.1.1 // indirect
|
||||||
github.com/anacrolix/chansync v0.7.0 // indirect
|
github.com/anacrolix/chansync v0.7.0 // indirect
|
||||||
github.com/anacrolix/dht/v2 v2.23.0 // indirect
|
|
||||||
github.com/anacrolix/envpprof v1.5.0 // indirect
|
github.com/anacrolix/envpprof v1.5.0 // indirect
|
||||||
github.com/anacrolix/generics v0.2.0 // indirect
|
github.com/anacrolix/generics v0.2.0 // indirect
|
||||||
github.com/anacrolix/go-libutp v1.4.0 // indirect
|
github.com/anacrolix/go-libutp v1.4.0 // indirect
|
||||||
|
|
@ -33,7 +34,6 @@ require (
|
||||||
github.com/anacrolix/multiless v0.4.0 // indirect
|
github.com/anacrolix/multiless v0.4.0 // indirect
|
||||||
github.com/anacrolix/stm v0.5.0 // indirect
|
github.com/anacrolix/stm v0.5.0 // indirect
|
||||||
github.com/anacrolix/sync v0.6.0 // indirect
|
github.com/anacrolix/sync v0.6.0 // indirect
|
||||||
github.com/anacrolix/upnp v0.1.4 // indirect
|
|
||||||
github.com/anacrolix/utp v0.2.0 // indirect
|
github.com/anacrolix/utp v0.2.0 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# unarr — Windows installer (PowerShell 5.1+)
|
# unarr — Windows installer (PowerShell 5.1+)
|
||||||
# Usage: irm https://get.unarr.com/install.ps1 | iex
|
# Usage: irm https://get.unarr.com/install.ps1 | iex
|
||||||
# or: irm https://raw.githubusercontent.com/torrentclaw/torrentclaw-cli/main/install.ps1 | iex
|
# or: irm https://raw.githubusercontent.com/torrentclaw/unarr/main/install.ps1 | iex
|
||||||
#
|
#
|
||||||
# Options (env vars):
|
# Options (env vars):
|
||||||
# $env:INSTALL_DIR = "C:\path" — where to place the binary
|
# $env:INSTALL_DIR = "C:\path" — where to place the binary
|
||||||
|
|
@ -15,7 +15,7 @@ param(
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
$Repo = "torrentclaw/torrentclaw-cli"
|
$Repo = "torrentclaw/unarr"
|
||||||
$Binary = "unarr.exe"
|
$Binary = "unarr.exe"
|
||||||
|
|
||||||
# ---- Helpers ----
|
# ---- Helpers ----
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# unarr — cross-platform installer (Linux / macOS)
|
# unarr — cross-platform installer (Linux / macOS)
|
||||||
# Usage: curl -fsSL https://get.unarr.com/install.sh | sh
|
# Usage: curl -fsSL https://get.unarr.com/install.sh | sh
|
||||||
# or: curl -fsSL https://raw.githubusercontent.com/torrentclaw/torrentclaw-cli/main/install.sh | sh
|
# or: curl -fsSL https://raw.githubusercontent.com/torrentclaw/unarr/main/install.sh | sh
|
||||||
#
|
#
|
||||||
# Options (env vars):
|
# Options (env vars):
|
||||||
# INSTALL_DIR=/usr/local/bin — where to place the binary (default: /usr/local/bin or ~/.local/bin)
|
# INSTALL_DIR=/usr/local/bin — where to place the binary (default: /usr/local/bin or ~/.local/bin)
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
# METHOD=binary|docker — force install method (default: auto-detect)
|
# METHOD=binary|docker — force install method (default: auto-detect)
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
REPO="torrentclaw/torrentclaw-cli"
|
REPO="torrentclaw/unarr"
|
||||||
BINARY="unarr"
|
BINARY="unarr"
|
||||||
|
|
||||||
# ---- Colors (only if terminal) ----
|
# ---- Colors (only if terminal) ----
|
||||||
|
|
@ -217,7 +217,7 @@ install_docker() {
|
||||||
torrentclaw/unarr
|
torrentclaw/unarr
|
||||||
|
|
||||||
# Or use the provided docker-compose.yml:
|
# Or use the provided docker-compose.yml:
|
||||||
# curl -fsSL https://raw.githubusercontent.com/torrentclaw/torrentclaw-cli/main/docker-compose.yml > docker-compose.yml
|
# curl -fsSL https://raw.githubusercontent.com/torrentclaw/unarr/main/docker-compose.yml > docker-compose.yml
|
||||||
# docker compose up -d
|
# docker compose up -d
|
||||||
|
|
||||||
DOCKER_USAGE
|
DOCKER_USAGE
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DaemonState is written to disk every heartbeat for external tools to read.
|
// DaemonState is written to disk every heartbeat for external tools to read.
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCleanCmd() *cobra.Command {
|
func newCleanCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/charmbracelet/huh"
|
"github.com/charmbracelet/huh"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configCategories = []string{"downloads", "organization", "notifications", "device", "region", "connection", "advanced"}
|
var configCategories = []string{"downloads", "organization", "notifications", "device", "region", "connection", "advanced"}
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/engine"
|
"github.com/torrentclaw/unarr/internal/engine"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/library"
|
"github.com/torrentclaw/unarr/internal/library"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/download"
|
"github.com/torrentclaw/unarr/internal/usenet/download"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/upgrade"
|
"github.com/torrentclaw/unarr/internal/upgrade"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newStartCmd creates the top-level `unarr start` command.
|
// newStartCmd creates the top-level `unarr start` command.
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDoctorCmd() *cobra.Command {
|
func newDoctorCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/engine"
|
"github.com/torrentclaw/unarr/internal/engine"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/parser"
|
"github.com/torrentclaw/unarr/internal/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDownloadCmd() *cobra.Command {
|
func newDownloadCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ import (
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/arr"
|
"github.com/torrentclaw/unarr/internal/arr"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/mediaserver"
|
"github.com/torrentclaw/unarr/internal/mediaserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInitCmd() *cobra.Command {
|
func newInitCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/parser"
|
"github.com/torrentclaw/unarr/internal/parser"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInspectCmd() *cobra.Command {
|
func newInspectCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ import (
|
||||||
"github.com/charmbracelet/huh"
|
"github.com/charmbracelet/huh"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/arr"
|
"github.com/torrentclaw/unarr/internal/arr"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/mediaserver"
|
"github.com/torrentclaw/unarr/internal/mediaserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMigrateCmd() *cobra.Command {
|
func newMigrateCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newPopularCmd() *cobra.Command {
|
func newPopularCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRecentCmd() *cobra.Command {
|
func newRecentCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReloadableConfig holds a reference to the daemon for hot-reload.
|
// ReloadableConfig holds a reference to the daemon for hot-reload.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import "github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
import "github.com/torrentclaw/unarr/internal/agent"
|
||||||
|
|
||||||
// ReloadableConfig holds a reference to the daemon for hot-reload.
|
// ReloadableConfig holds a reference to the daemon for hot-reload.
|
||||||
type ReloadableConfig struct {
|
type ReloadableConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/sentry"
|
"github.com/torrentclaw/unarr/internal/sentry"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ Get started:
|
||||||
unarr start Start the download daemon
|
unarr start Start the download daemon
|
||||||
|
|
||||||
Documentation: https://torrentclaw.com/cli
|
Documentation: https://torrentclaw.com/cli
|
||||||
Source: https://github.com/torrentclaw/torrentclaw-cli`,
|
Source: https://github.com/torrentclaw/unarr`,
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
if noColor || os.Getenv("NO_COLOR") != "" {
|
if noColor || os.Getenv("NO_COLOR") != "" {
|
||||||
color.NoColor = true
|
color.NoColor = true
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/library"
|
"github.com/torrentclaw/unarr/internal/library"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newScanCmd() *cobra.Command {
|
func newScanCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newSearchCmd() *cobra.Command {
|
func newSearchCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/upgrade"
|
"github.com/torrentclaw/unarr/internal/upgrade"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newSelfUpdateCmd() *cobra.Command {
|
func newSelfUpdateCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStatsCmd() *cobra.Command {
|
func newStatsCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ import (
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/engine"
|
"github.com/torrentclaw/unarr/internal/engine"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/parser"
|
"github.com/torrentclaw/unarr/internal/parser"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStreamCmd() *cobra.Command {
|
func newStreamCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/engine"
|
"github.com/torrentclaw/unarr/internal/engine"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// streamRegistry tracks active stream tasks and servers for cancellation.
|
// streamRegistry tracks active stream tasks and servers for cancellation.
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ func newStubCmd(name, short string) *cobra.Command {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
color.New(color.FgYellow).Printf(" ⚠️ '%s' is coming in a future release.\n", name)
|
color.New(color.FgYellow).Printf(" ⚠️ '%s' is coming in a future release.\n", name)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println(" Follow progress at: https://github.com/torrentclaw/torrentclaw-cli")
|
fmt.Println(" Follow progress at: https://github.com/torrentclaw/unarr")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
tc "github.com/torrentclaw/go-client"
|
tc "github.com/torrentclaw/go-client"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/ui"
|
"github.com/torrentclaw/unarr/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newWatchCmd() *cobra.Command {
|
func newWatchCmd() *cobra.Command {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDebridAvailable(t *testing.T) {
|
func TestDebridAvailable(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ManagerConfig holds download manager settings.
|
// ManagerConfig holds download manager settings.
|
||||||
|
|
@ -24,6 +24,7 @@ type Manager struct {
|
||||||
|
|
||||||
activeMu sync.RWMutex
|
activeMu sync.RWMutex
|
||||||
active map[string]*Task
|
active map[string]*Task
|
||||||
|
cancels map[string]context.CancelFunc // per-task cancel functions
|
||||||
|
|
||||||
sem chan struct{}
|
sem chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
@ -45,6 +46,7 @@ func NewManager(cfg ManagerConfig, reporter *ProgressReporter, downloaders ...Do
|
||||||
reporter: reporter,
|
reporter: reporter,
|
||||||
downloaders: dlMap,
|
downloaders: dlMap,
|
||||||
active: make(map[string]*Task),
|
active: make(map[string]*Task),
|
||||||
|
cancels: make(map[string]context.CancelFunc),
|
||||||
sem: make(chan struct{}, cfg.MaxConcurrent),
|
sem: make(chan struct{}, cfg.MaxConcurrent),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,8 +55,12 @@ func NewManager(cfg ManagerConfig, reporter *ProgressReporter, downloaders ...Do
|
||||||
func (m *Manager) Submit(ctx context.Context, at agent.Task) {
|
func (m *Manager) Submit(ctx context.Context, at agent.Task) {
|
||||||
task := NewTaskFromAgent(at)
|
task := NewTaskFromAgent(at)
|
||||||
|
|
||||||
|
// Per-task cancellable context so CancelTask can unblock the goroutine
|
||||||
|
taskCtx, taskCancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
m.activeMu.Lock()
|
m.activeMu.Lock()
|
||||||
m.active[task.ID] = task
|
m.active[task.ID] = task
|
||||||
|
m.cancels[task.ID] = taskCancel
|
||||||
m.activeMu.Unlock()
|
m.activeMu.Unlock()
|
||||||
|
|
||||||
m.reporter.Track(task)
|
m.reporter.Track(task)
|
||||||
|
|
@ -65,7 +71,8 @@ func (m *Manager) Submit(ctx context.Context, at agent.Task) {
|
||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer m.wg.Done()
|
defer m.wg.Done()
|
||||||
m.processTask(ctx, task)
|
defer taskCancel()
|
||||||
|
m.processTask(taskCtx, task)
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +81,7 @@ func (m *Manager) Submit(ctx context.Context, at agent.Task) {
|
||||||
select {
|
select {
|
||||||
case m.sem <- struct{}{}:
|
case m.sem <- struct{}{}:
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
taskCancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,7 +89,8 @@ func (m *Manager) Submit(ctx context.Context, at agent.Task) {
|
||||||
go func() {
|
go func() {
|
||||||
defer m.wg.Done()
|
defer m.wg.Done()
|
||||||
defer func() { <-m.sem }()
|
defer func() { <-m.sem }()
|
||||||
m.processTask(ctx, task)
|
defer taskCancel()
|
||||||
|
m.processTask(taskCtx, task)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,12 +128,19 @@ func (m *Manager) ActiveTasks() []*Task {
|
||||||
func (m *Manager) CancelTask(taskID string) {
|
func (m *Manager) CancelTask(taskID string) {
|
||||||
m.activeMu.RLock()
|
m.activeMu.RLock()
|
||||||
task, ok := m.active[taskID]
|
task, ok := m.active[taskID]
|
||||||
|
cancel := m.cancels[taskID]
|
||||||
m.activeMu.RUnlock()
|
m.activeMu.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cancel the task's context first — this unblocks the goroutine
|
||||||
|
// (e.g. stuck waiting for metadata) so it exits and releases the semaphore slot.
|
||||||
|
if cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
||||||
dl.Pause(taskID) // stop download, keep files
|
dl.Pause(taskID) // stop download, keep files
|
||||||
}
|
}
|
||||||
|
|
@ -141,12 +157,17 @@ func (m *Manager) CancelTask(taskID string) {
|
||||||
func (m *Manager) PauseTask(taskID string) {
|
func (m *Manager) PauseTask(taskID string) {
|
||||||
m.activeMu.RLock()
|
m.activeMu.RLock()
|
||||||
task, ok := m.active[taskID]
|
task, ok := m.active[taskID]
|
||||||
|
cancel := m.cancels[taskID]
|
||||||
m.activeMu.RUnlock()
|
m.activeMu.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
||||||
dl.Pause(taskID) // stop download, keep files for resume
|
dl.Pause(taskID) // stop download, keep files for resume
|
||||||
}
|
}
|
||||||
|
|
@ -159,12 +180,17 @@ func (m *Manager) PauseTask(taskID string) {
|
||||||
func (m *Manager) CancelAndDeleteFiles(taskID string) {
|
func (m *Manager) CancelAndDeleteFiles(taskID string) {
|
||||||
m.activeMu.RLock()
|
m.activeMu.RLock()
|
||||||
task, ok := m.active[taskID]
|
task, ok := m.active[taskID]
|
||||||
|
cancel := m.cancels[taskID]
|
||||||
m.activeMu.RUnlock()
|
m.activeMu.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
if dl, exists := m.downloaders[task.ResolvedMethod]; exists {
|
||||||
dl.Cancel(taskID) // stop download + delete files
|
dl.Cancel(taskID) // stop download + delete files
|
||||||
}
|
}
|
||||||
|
|
@ -204,8 +230,12 @@ func (m *Manager) Shutdown(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean active map
|
// Clean active map and cancel functions
|
||||||
m.activeMu.Lock()
|
m.activeMu.Lock()
|
||||||
|
for id, cancel := range m.cancels {
|
||||||
|
cancel()
|
||||||
|
delete(m.cancels, id)
|
||||||
|
}
|
||||||
m.active = make(map[string]*Task)
|
m.active = make(map[string]*Task)
|
||||||
m.activeMu.Unlock()
|
m.activeMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +244,7 @@ func (m *Manager) processTask(ctx context.Context, task *Task) {
|
||||||
defer func() {
|
defer func() {
|
||||||
m.activeMu.Lock()
|
m.activeMu.Lock()
|
||||||
delete(m.active, task.ID)
|
delete(m.active, task.ID)
|
||||||
|
delete(m.cancels, task.ID)
|
||||||
m.activeMu.Unlock()
|
m.activeMu.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestManagerSubmitAndWait(t *testing.T) {
|
func TestManagerSubmitAndWait(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ActionFunc is called when the server signals an action on a task.
|
// ActionFunc is called when the server signals an action on a task.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskStatus represents the current state of a download task.
|
// TaskStatus represents the current state of a download task.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package engine
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewTaskFromAgent(t *testing.T) {
|
func TestNewTaskFromAgent(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"github.com/anacrolix/dht/v2/krpc"
|
"github.com/anacrolix/dht/v2/krpc"
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/anacrolix/torrent/storage"
|
"github.com/anacrolix/torrent/storage"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,13 @@ type UPnPMapping struct {
|
||||||
// setupUPnP discovers the gateway, maps the port, and gets the public IP.
|
// setupUPnP discovers the gateway, maps the port, and gets the public IP.
|
||||||
// Returns nil if UPnP is not available or fails.
|
// Returns nil if UPnP is not available or fails.
|
||||||
func setupUPnP(internalPort int) (*UPnPMapping, error) {
|
func setupUPnP(internalPort int) (*UPnPMapping, error) {
|
||||||
devices := upnp.Discover(0, 5*time.Second, alog.Logger{})
|
log.Println("stream: discovering UPnP gateway (10s timeout)...")
|
||||||
|
devices := upnp.Discover(0, 10*time.Second, alog.Logger{})
|
||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
return nil, fmt.Errorf("no UPnP devices found")
|
return nil, fmt.Errorf("no UPnP devices found (is UPnP enabled on your router?)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("stream: found %d UPnP device(s), using %s", len(devices), devices[0].ID())
|
||||||
device := devices[0]
|
device := devices[0]
|
||||||
|
|
||||||
// Get public IP
|
// Get public IP
|
||||||
|
|
@ -32,13 +34,15 @@ func setupUPnP(internalPort int) (*UPnPMapping, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get external IP: %w", err)
|
return nil, fmt.Errorf("get external IP: %w", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("stream: public IP via UPnP: %s", externalIP)
|
||||||
|
|
||||||
// Map port (0 = let router choose external port, 2h lease)
|
// Map port (same internal/external, 2h lease)
|
||||||
mappedPort, err := device.AddPortMapping(upnp.TCP, internalPort, internalPort, "unarr stream", 2*time.Hour)
|
mappedPort, err := device.AddPortMapping(upnp.TCP, internalPort, internalPort, "unarr stream", 2*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("add port mapping: %w", err)
|
return nil, fmt.Errorf("add port mapping %d: %w", internalPort, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("stream: UPnP port mapped %s:%d -> local:%d (2h lease)", externalIP, mappedPort, internalPort)
|
||||||
return &UPnPMapping{
|
return &UPnPMapping{
|
||||||
ExternalIP: externalIP.String(),
|
ExternalIP: externalIP.String(),
|
||||||
ExternalPort: mappedPort,
|
ExternalPort: mappedPort,
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
"github.com/torrentclaw/unarr/internal/agent"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/download"
|
"github.com/torrentclaw/unarr/internal/usenet/download"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nntp"
|
"github.com/torrentclaw/unarr/internal/usenet/nntp"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nzb"
|
"github.com/torrentclaw/unarr/internal/usenet/nzb"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/postprocess"
|
"github.com/torrentclaw/unarr/internal/usenet/postprocess"
|
||||||
)
|
)
|
||||||
|
|
||||||
// activeDownload holds the state for a single in-progress usenet download.
|
// activeDownload holds the state for a single in-progress usenet download.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/config"
|
"github.com/torrentclaw/unarr/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CachePath returns the default library cache file path.
|
// CachePath returns the default library cache file path.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/library/mediainfo"
|
"github.com/torrentclaw/unarr/internal/library/mediainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package library
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/library/mediainfo"
|
"github.com/torrentclaw/unarr/internal/library/mediainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveResolution(t *testing.T) {
|
func TestResolveResolution(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/library/mediainfo"
|
"github.com/torrentclaw/unarr/internal/library/mediainfo"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/parser"
|
"github.com/torrentclaw/unarr/internal/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// videoExts are file extensions considered as video files.
|
// videoExts are file extensions considered as video files.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package library
|
package library
|
||||||
|
|
||||||
import "github.com/torrentclaw/torrentclaw-cli/internal/agent"
|
import "github.com/torrentclaw/unarr/internal/agent"
|
||||||
|
|
||||||
// BuildSyncItems converts cached library items to sync request items.
|
// BuildSyncItems converts cached library items to sync request items.
|
||||||
// Shared between unarr scan (cmd/scan.go) and auto-scan (cmd/daemon.go).
|
// Shared between unarr scan (cmd/scan.go) and auto-scan (cmd/daemon.go).
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package library
|
package library
|
||||||
|
|
||||||
import "github.com/torrentclaw/torrentclaw-cli/internal/library/mediainfo"
|
import "github.com/torrentclaw/unarr/internal/library/mediainfo"
|
||||||
|
|
||||||
// LibraryItem represents a single scanned media file.
|
// LibraryItem represents a single scanned media file.
|
||||||
type LibraryItem struct {
|
type LibraryItem struct {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// dsn is injected at build time via ldflags. If empty, Sentry is disabled.
|
// dsn is injected at build time via ldflags. If empty, Sentry is disabled.
|
||||||
// Set via: -ldflags "-X github.com/torrentclaw/torrentclaw-cli/internal/sentry.dsn=..."
|
// Set via: -ldflags "-X github.com/torrentclaw/unarr/internal/sentry.dsn=..."
|
||||||
var dsn string
|
var dsn string
|
||||||
|
|
||||||
const flushTimeout = 2 * time.Second
|
const flushTimeout = 2 * time.Second
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nntp"
|
"github.com/torrentclaw/unarr/internal/usenet/nntp"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nzb"
|
"github.com/torrentclaw/unarr/internal/usenet/nzb"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/yenc"
|
"github.com/torrentclaw/unarr/internal/usenet/yenc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Progress is emitted during download.
|
// Progress is emitted during download.
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/download"
|
"github.com/torrentclaw/unarr/internal/usenet/download"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nntp"
|
"github.com/torrentclaw/unarr/internal/usenet/nntp"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nzb"
|
"github.com/torrentclaw/unarr/internal/usenet/nzb"
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/postprocess"
|
"github.com/torrentclaw/unarr/internal/usenet/postprocess"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestE2EDownload is a real end-to-end test that downloads from Usenet.
|
// TestE2EDownload is a real end-to-end test that downloads from Usenet.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nzb"
|
"github.com/torrentclaw/unarr/internal/usenet/nzb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Binary progress file format:
|
// Binary progress file format:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/torrentclaw/torrentclaw-cli/internal/usenet/nzb"
|
"github.com/torrentclaw/unarr/internal/usenet/nzb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fixedPast = time.Now().Add(-30 * 24 * time.Hour)
|
var fixedPast = time.Now().Add(-30 * 24 * time.Hour)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue