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
This commit is contained in:
parent
0b6c6849b1
commit
677a8fe083
34 changed files with 4766 additions and 22 deletions
84
internal/arr/discovery_test.go
Normal file
84
internal/arr/discovery_test.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package arr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseConfigXML(t *testing.T) {
|
||||
xml := `<Config>
|
||||
<Port>8989</Port>
|
||||
<ApiKey>abc123def456</ApiKey>
|
||||
<UrlBase>/sonarr</UrlBase>
|
||||
</Config>`
|
||||
|
||||
port, apiKey, urlBase := parseConfigXML(strings.NewReader(xml))
|
||||
if port != "8989" {
|
||||
t.Errorf("port = %q, want 8989", port)
|
||||
}
|
||||
if apiKey != "abc123def456" {
|
||||
t.Errorf("apiKey = %q, want abc123def456", apiKey)
|
||||
}
|
||||
if urlBase != "/sonarr" {
|
||||
t.Errorf("urlBase = %q, want /sonarr", urlBase)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConfigXML_Minimal(t *testing.T) {
|
||||
xml := `<Config><Port>7878</Port><ApiKey>key</ApiKey></Config>`
|
||||
|
||||
port, apiKey, urlBase := parseConfigXML(strings.NewReader(xml))
|
||||
if port != "7878" || apiKey != "key" || urlBase != "" {
|
||||
t.Errorf("got port=%q apiKey=%q urlBase=%q", port, apiKey, urlBase)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConfigXML_Invalid(t *testing.T) {
|
||||
port, apiKey, _ := parseConfigXML(strings.NewReader("not xml"))
|
||||
if port != "" || apiKey != "" {
|
||||
t.Errorf("invalid XML should return empty values")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHostPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
ports string
|
||||
container string
|
||||
want string
|
||||
}{
|
||||
{"0.0.0.0:8989->8989/tcp", "8989", "8989"},
|
||||
{"0.0.0.0:9090->8989/tcp, :::9090->8989/tcp", "8989", "9090"},
|
||||
{"0.0.0.0:7878->7878/tcp", "7878", "7878"},
|
||||
{"", "8989", ""},
|
||||
{"0.0.0.0:3000->3000/tcp", "8989", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.ports, func(t *testing.T) {
|
||||
got := extractHostPort(tt.ports, tt.container)
|
||||
if got != tt.want {
|
||||
t.Errorf("extractHostPort(%q, %q) = %q, want %q", tt.ports, tt.container, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectApp(t *testing.T) {
|
||||
tests := []struct {
|
||||
image string
|
||||
want string
|
||||
}{
|
||||
{"linuxserver/sonarr:latest", "sonarr"},
|
||||
{"hotio/radarr", "radarr"},
|
||||
{"ghcr.io/linuxserver/prowlarr:develop", "prowlarr"},
|
||||
{"nginx:latest", ""},
|
||||
{"postgres:16", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.image, func(t *testing.T) {
|
||||
got := detectApp(tt.image)
|
||||
if got != tt.want {
|
||||
t.Errorf("detectApp(%q) = %q, want %q", tt.image, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue