Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ coverage.html
*.jpeg
*.gif
WarpPreview*

# HAR files (contain sensitive API keys and system paths)
*.har
127 changes: 114 additions & 13 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,120 @@
# Repository Guidelines
# AGENTS.md - AI Coding Agent Guidelines

## Project Structure & Module Organization
CalmsToolkit is a Go module that ships three CLI binaries from the root: `media-requests.go`, `media-streams.go`, and `media-calendar.go`. Supporting assets live alongside them; research notes are under `docs/`, example flows in `EXAMPLES_MEDIA_REQUESTS.md`, and reusable env defaults in `.env.example`. The `bin/` directory is generated by builds, while the Makefile drives reproducible compilation.
**Updated:** 2026-01-27 | **Repo:** CalmsToolkit | **Branch:** feature/queue-remediation-tool

## Build, Test, and Development Commands
Use the Make targets to stay consistent: `make build` compiles the three binaries for your host, `make build-all` cross-compiles for common OS/arch pairs, `make test` runs `go test ./...`, and `make tidy` refreshes module metadata. During iteration you can run a tool directly, e.g. `go run -tags mediarequests media-requests.go -url ...`, but commit the compiled output only to `bin/`.
## Overview
CalmsToolkit is a flat, multi-binary Go CLI toolkit for media server management
(Plex/Jellyfin streams, Overseerr requests, Sonarr/Radarr queues). Each tool is
enabled via build tags and compiled as a separate binary.

## Coding Style & Naming Conventions
Format every change with `gofmt` (or `go fmt ./...`) before committing; the project mirrors idiomatic Go spacing and grouping. Keep exported types, funcs, and consts in UpperCamelCase with doc comments, use lowerCamelCase for locals, and reserve ALL_CAPS for environment variable labels surfaced to users. Group related constants and config structs to keep CLI flag wiring readable.
## Build, Test, Tidy
```bash
make build # Build all binaries into bin/
make build-all # Cross-compile (linux/darwin/windows/freebsd × amd64/arm64)
make install # Install to /usr/local/bin (configurable via prefix)
make clean # Remove build artifacts
make test # Run tagged test suites sequentially
make tidy # go mod tidy
go fmt ./... # REQUIRED before commit
```

## Testing Guidelines
Place table-driven tests in `_test.go` files beside the feature under test and cover both happy paths and API failure modes (mock HTTP responses where feasible). Target at least the functionality added in each PR; run `make test` or `go test ./...` before pushing, and capture notable coverage regressions in the PR notes.
### Single Test / Single Tool
```bash
# Run a single test by name (tag required)
go test -tags mediarequests -v -run TestFunctionName ./...
go test -tags queueremediation -v -run TestFunctionName ./...

## Commit & Pull Request Guidelines
Match the existing history by writing imperative, present-tense commit subjects (`Add media calendar filters`). Squash incidental fixups locally so each commit is reviewable. Pull requests should link the relevant issue, summarize behavioral changes, call out new flags or env vars, and include screenshots or sample CLI output when they clarify UX changes. Flag any security-sensitive configuration updates explicitly.
# Build a single tool
go build -tags mediastreams -o bin/media-streams media-streams.go
go build -tags queueremediation -o bin/queue-remediation \
queue-remediation.go queue-remediation-shared.go queue-remediation-tui.go
```

## Configuration & Secrets
Copy `.env.example` into a private `.env` when experimenting; never check real Overseerr, Plex, or Jellyfin tokens into Git. Local overrides should rely on shell exports or direnv, and scrub logs or fixtures before sharing them in reviews.
### Linting
No dedicated linter is configured. Use `go fmt ./...` for formatting and keep
the codebase consistent with existing patterns.

## Project Structure
```
./
├── media-requests.go # Overseerr/Jellyseerr interactive menu (tag: mediarequests)
├── media-streams.go # Plex/Jellyfin unified monitor (tag: mediastreams)
├── media-calendar.go # Sonarr/Radarr upcoming calendar (tag: mediacalendar)
├── arr-feed.go # Sonarr/Radarr activity feed (tag: arrfeed)
├── queue-remediation*.go # Queue fixer + TUI (tag: queueremediation) [3 files]
├── *_test.go # Co-located tests with matching build tags
├── tooling_stub.go # Empty stub for untagged builds/IDE support
├── docs/ # Research, API docs, implementation guides
└── bin/ # Build output (gitignored)
```

## Build Tag Matrix
| Binary | Tag | Source Files |
|--------|-----|--------------|
| media-requests | `mediarequests` | `media-requests.go` |
| media-streams | `mediastreams` | `media-streams.go` |
| media-calendar | `mediacalendar` | `media-calendar.go` |
| arr-feed | `arrfeed` | `arr-feed.go` |
| queue-remediation | `queueremediation` | `queue-remediation.go`, `queue-remediation-shared.go`, `queue-remediation-tui.go` |

## Code Style
- **Imports**: stdlib first, blank line, then external (tablewriter, bubbletea, lipgloss)
- **Formatting**: always run `go fmt ./...` after edits
- **Build tags**: keep `//go:build <tag>` at top of tool files and tests
- **Types**: exported = UpperCamelCase + doc comment; unexported = lowerCamelCase
- **Constants**: ALL_CAPS for user-facing env vars (`OVERSEERR_TOKEN`)
- **Internal consts**: UpperCamelCase (`StatusPending`)
- **Naming**: avoid stuttering (`request.ID` not `request.RequestID`)
- **Receivers**: 1-2 letters, consistent per type
- **Struct tags**: prefer explicit JSON tags, omit with `json:"-"` when needed

## Error Handling
- Always check and return errors; avoid `_` and empty catch blocks
- Prefer explicit `if err != nil { ... }` blocks
- For CLI exits, print errors to stderr and exit non-zero
- Dry-run mode must not mutate state

## Logging
- CLI output is primarily user-facing; keep logs clear and minimal
- Prefer stderr for error messages

## Testing Patterns
- **Table-driven** tests: `tests := []struct { name string; ... }` with `t.Run()`
- **HTTP mocking**: `httptest.NewServer` for API tests
- **Tag coupling**: test files MUST match source file build tags
- **Coverage**: happy path + error cases (400/401/404/500) + edge cases
- **HAR-aligned**: queue remediation uses real Sonarr/Radarr captures for test data

## Configuration Hierarchy
1. `.env` file at `/opt/apps/compose/.env` (lowest priority)
2. Environment variables (`PLEX_URL`, `SONARR_TOKENS`, etc.)
3. Command-line flags (highest priority)

Multi-instance support: `SONARR_URLS=url1,url2` with matching `SONARR_TOKENS=token1,token2`

## Key Dependencies
- `charmbracelet/bubbletea` + `lipgloss`: TUI framework (media-requests, queue-remediation)
- `olekukonko/tablewriter`: Table formatting
- `golang.org/x/term`: Terminal detection

## API Patterns
- **Sonarr/Radarr**: always include `X-Api-Key` header
- **Manual Import**: use `/api/v3/command` with `ManualImport` name
- **Overseerr**: fallback logic for Bug #3949 (pagination issues)
- **Plex**: XML API at `/status/sessions`; Jellyfin: JSON at `/Sessions`

## Anti-Patterns (Never)
- Commit secrets (tokens in `.env.example` are placeholders only)
- Suppress errors with `_` or empty catch blocks
- Create files without matching build tags when adding to a tool
- Skip `go fmt` before commit
- Modify state in dry-run mode paths

## Documentation Index
- `docs/README_*.md` - tool-specific usage guides
- `docs/SONARR_RADARR_QUEUE_API*.md` - queue API reference
- `docs/MANUAL_IMPORT_*.md` - import workflow research
- `docs/tui_uplift/` - TUI modernization plan

## Cursor/Copilot Rules
- No `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` files found.
25 changes: 16 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ BINARY_STREAMSTOOL=media-streams
BINARY_CALENDAR=media-calendar
BINARY_REQUESTS=media-requests
BINARY_ARRFEED=arr-feed
BINARY_QUEUEREMEDIATION=queue-remediation

# Go parameters
GOCMD=go
Expand Down Expand Up @@ -40,6 +41,7 @@ build:
$(GOBUILD) $(LDFLAGS) -tags mediacalendar -o $(BUILD_DIR)/$(BINARY_CALENDAR) media-calendar.go
$(GOBUILD) $(LDFLAGS) -tags mediarequests -o $(BUILD_DIR)/$(BINARY_REQUESTS) media-requests.go
$(GOBUILD) $(LDFLAGS) -tags arrfeed -o $(BUILD_DIR)/$(BINARY_ARRFEED) arr-feed.go
$(GOBUILD) $(LDFLAGS) -tags queueremediation -o $(BUILD_DIR)/$(BINARY_QUEUEREMEDIATION) queue-remediation.go queue-remediation-shared.go queue-remediation-tui.go
@echo "Build complete: $(BUILD_DIR)/*"

build-all:
Expand All @@ -48,17 +50,18 @@ build-all:

@for GOOS in linux darwin windows freebsd; do \
for GOARCH in amd64 arm64; do \
for SRC in media-streams media-calendar media-requests arr-feed; do \
BIN=$${SRC}-$$GOOS-$$GOARCH; \
EXT=$${GOOS} = "windows" && EXT=".exe" || EXT=""; \
case $$SRC in \
media-streams) TAG=mediastreams ;; \
media-calendar) TAG=mediacalendar ;; \
media-requests) TAG=mediarequests ;; \
arr-feed) TAG=arrfeed ;; \
for SRC in media-streams media-calendar media-requests arr-feed queue-remediation; do \
BIN=$${SRC}-$$GOOS-$$GOARCH; \
if [ "$$GOOS" = "windows" ]; then EXT=".exe"; else EXT=""; fi; \
case $$SRC in \
media-streams) TAG=mediastreams; EXTRA_FILES="" ;; \
media-calendar) TAG=mediacalendar; EXTRA_FILES="" ;; \
media-requests) TAG=mediarequests; EXTRA_FILES="" ;; \
arr-feed) TAG=arrfeed; EXTRA_FILES="" ;; \
queue-remediation) TAG=queueremediation; EXTRA_FILES="queue-remediation-shared.go queue-remediation-tui.go" ;; \
esac; \
echo "Building $$SRC for $$GOOS/$$GOARCH..."; \
GOOS=$$GOOS GOARCH=$$GOARCH $(GOBUILD) $(LDFLAGS) -tags $$TAG -o $(BUILD_DIR)/$$BIN$$EXT $$SRC.go || exit 1; \
GOOS=$$GOOS GOARCH=$$GOARCH $(GOBUILD) $(LDFLAGS) -tags $$TAG -o $(BUILD_DIR)/$$BIN$$EXT $$SRC.go $$EXTRA_FILES || exit 1; \
done; \
done; \
done
Expand All @@ -72,6 +75,7 @@ install: build
@install -m 755 $(BUILD_DIR)/$(BINARY_CALENDAR) $(INSTALL_DIR)/$(BINARY_CALENDAR)
@install -m 755 $(BUILD_DIR)/$(BINARY_REQUESTS) $(INSTALL_DIR)/$(BINARY_REQUESTS)
@install -m 755 $(BUILD_DIR)/$(BINARY_ARRFEED) $(INSTALL_DIR)/$(BINARY_ARRFEED)
@install -m 755 $(BUILD_DIR)/$(BINARY_QUEUEREMEDIATION) $(INSTALL_DIR)/$(BINARY_QUEUEREMEDIATION)
@echo "Make sure $(INSTALL_DIR) is in your PATH."
@echo "Installation complete!"

Expand All @@ -94,6 +98,9 @@ test:
@echo "Running arr-feed tests..."
$(GOTEST) -tags arrfeed -v ./...
@echo ""
@echo "Running queue-remediation tests..."
$(GOTEST) -tags queueremediation -v ./...
@echo ""
@echo "All tests complete!"

tidy:
Expand Down
117 changes: 117 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,123 @@ make build
./bin/media-streams -json | jq '.total_streams'
```

### Queue Remediation

An intelligent tool for automatically detecting and fixing stuck queue items in Sonarr and Radarr. Identifies problematic downloads (failed imports, quality mismatches, sample files) and applies appropriate remediation actions using a corrected API implementation based on live Sonarr testing.

#### Features

- 🔍 **Smart Detection**: Automatically identifies stuck/blocked queue items using advanced status message parsing
- 🎯 **Targeted Actions**: Deletes failed items, triggers manual imports, or monitors active downloads
- 🖥️ **Interactive TUI**: Full-featured terminal UI mode for manual review and action selection
- 🛡️ **Safe Mode**: Dry-run mode shows what would happen without making changes
- 🔄 **Multi-Instance**: Supports multiple Sonarr and Radarr servers simultaneously
- 🔧 **Corrected API Implementation**: Fixed to match Sonarr's actual API behavior with proper Command structure
- 🚨 **Blocklist Management**: Automatically blocklists problematic releases
- 📝 **Detailed Logging**: Verbose and debug modes with comprehensive API call tracking
- ⚡ **Fast**: Processes entire queue in seconds with parallel instance handling
- 🔗 **DownloadId Support**: Properly maps download IDs from queue items to import requests

#### Quick Start

```bash
# Build binary
make build

# Set up configuration (multiple instances supported)
export SONARR_URLS="http://localhost:8989,http://sonarr2:8989"
export SONARR_TOKENS="token1,token2"
export RADARR_URLS="http://localhost:7878"
export RADARR_TOKENS="your-api-key"

# Dry-run mode (shows what would happen without making changes)
./bin/queue-remediation -dry-run

# Run with verbose logging to see API call details
./bin/queue-remediation -verbose

# Use enhanced REST API for manual imports (recommended)
./bin/queue-remediation -use-rest-api -verbose

# Interactive TUI mode for manual review
./bin/queue-remediation -manual

# Debug mode for troubleshooting (shows full API payloads)
./bin/queue-remediation -debug -verbose
```

#### Manual Import Process

The queue remediation tool uses a corrected hybrid approach based on live Sonarr API analysis:

### Primary: Enhanced REST API (Fixed)
1. **Scan**: `/api/v3/manualimport?folder={path}&seriesId={id}` or `/api/v3/manualimport?folder={path}&movieId={id}`
2. **Filter**: Only process files matching queue item's series/movie ID with proper validation
3. **Import**: `/api/v3/command` POST with `ManualImport` command structure (corrected endpoint)

### Fallback: Command API (Enhanced)
- Uses `ManualImportCommand` wrapper with proper `name`, `files`, and `importMode` fields
- Includes `downloadId` parameter mapping from queue items
- Asynchronous execution with command ID tracking

#### API Structure Fixes

Based on live testing of Sonarr's web interface, the tool now correctly implements:

```json
{
"name": "ManualImport",
"files": [
{
"path": "/path/to/file.mkv",
"seriesId": 123,
"movieId": 456,
"downloadId": "queue-download-id",
"importMode": "auto"
}
],
"importMode": "auto"
}
```

#### Remediation Actions

The tool intelligently classifies queue items and applies appropriate actions:

- **DELETE + BLOCKLIST**: Quality/custom format mismatches, sample files, failed downloads
- **MANUAL_IMPORT**: Completed downloads stuck in import-blocked state (with ID validation)
- **MONITOR**: Active downloads progressing normally (no action needed)

#### TUI Mode Controls

When using `-manual` flag, the interactive terminal UI provides:

- **↑/↓**: Navigate between queue items
- **Enter**: Apply suggested action for current item
- **d**: Delete item (with blocklist option)
- **m**: Trigger manual import
- **s**: Skip/Monitor item (no action)
- **r**: Refresh queue data from all instances
- **q**: Quit TUI

The TUI shows detailed item information, suggested actions, API call progress, and real-time feedback for all operations.

#### Recent Improvements

- **✅ Fixed API Endpoint**: Changed from incorrect `/api/v3/manualimport` to correct `/api/v3/command`
- **✅ Correct Command Structure**: Implemented proper `ManualImportCommand` wrapper matching Sonarr's API
- **✅ DownloadId Support**: Added proper mapping from queue items to import requests
- **✅ Radarr Fixes**: Corrected MovieID usage and removed episode-specific fields for movies
- **✅ Enhanced Logging**: Added comprehensive API call tracking and debug output
- **✅ Full Test Coverage**: 800+ lines of tests validating all API fixes

#### Documentation

- **[README_QUEUEFIX.md](docs/README_QUEUEFIX.md)** - Complete usage guide and troubleshooting
- **[QUEUE_REMEDIATION_IMPLEMENTATION_GUIDE.md](docs/QUEUE_REMEDIATION_IMPLEMENTATION_GUIDE.md)** - Technical implementation details
- **[SONARR_RADARR_QUEUE_API.md](docs/SONARR_RADARR_QUEUE_API.md)** - API reference
- **[MANUAL_IMPORT_API_RESEARCH.md](docs/MANUAL_IMPORT_API_RESEARCH.md)** - Live API testing results and fixes

### Plex Streams Monitor

A high-performance Go-based tool for monitoring active Plex streams with detailed information about users, transcoding status, bandwidth, and quality.
Expand Down
Loading