Skip to content
Merged
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
30 changes: 26 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
FROM python:3.9-slim
WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Install Flask for the web interface
RUN pip install --no-cache-dir flask

# Copy application files
COPY main.py config.py api.py state.py ./
COPY missing.py upgrade.py ./
COPY web_server.py ./
COPY utils/ ./utils/
# Create state directory

# Create templates directory and copy index.html
RUN mkdir -p templates
COPY templates/ ./templates/

# Create required directories
RUN mkdir -p /tmp/huntarr-state
RUN mkdir -p /tmp/huntarr-logs

# Default environment variables
ENV API_KEY="your-api-key" \
API_URL="http://your-sonarr-address:8989" \
Expand All @@ -19,6 +32,15 @@ ENV API_KEY="your-api-key" \
STATE_RESET_INTERVAL_HOURS=168 \
RANDOM_SELECTION="true" \
MONITORED_ONLY="true" \
DEBUG_MODE="false"
# Run the application
CMD ["python", "main.py"]
DEBUG_MODE="false" \
ENABLE_WEB_UI="true"

# Expose web interface port
EXPOSE 8988

# Add startup script that conditionally starts the web UI
COPY start.sh .
RUN chmod +x start.sh

# Run the startup script which will decide what to launch
CMD ["./start.sh"]
115 changes: 95 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@

**NOTE**: This utilizes Sonarr API Version - `5`. Legacy name of this program: Sonarr Hunter.

---

**Change Log:**
Visit: https://github.com/plexguide/Huntarr-Sonarr/releases/

## Table of Contents
- [Overview](#overview)
- [Related Projects](#related-projects)
- [Features](#features)
- [How It Works](#how-it-works)
- [Configuration Options](#configuration-options)
- [Web Interface](#web-interface)
- [Installation Methods](#installation-methods)
- [Docker Run](#docker-run)
- [Docker Compose](#docker-compose)
Expand Down Expand Up @@ -55,6 +61,7 @@ My 12-year-old daughter is passionate about singing, dancing, and exploring STEM
- 🔁 **State Tracking**: Remembers which shows and episodes have been processed to avoid duplicate searches
- ⚙️ **Configurable Reset Timer**: Automatically resets search history after a configurable period
- 📦 **Modular Design**: Modern codebase with separated concerns for easier maintenance
- 🌐 **Web Interface**: Real-time log viewer with day/night mode (new!)

## Indexers Approving of Huntarr:
* https://ninjacentral.co.za
Expand Down Expand Up @@ -110,8 +117,14 @@ The following environment variables can be configured:
| `RANDOM_SELECTION` | Use random selection (`true`) or sequential (`false`) | true |
| `STATE_RESET_INTERVAL_HOURS` | Hours which the processed state files reset (168=1 week, 0=never reset) | 168 |
| `DEBUG_MODE` | Enable detailed debug logging (`true` or `false`) | false |
| `ENABLE_WEB_UI` | Enable or disable the web interface (`true` or `false`) | true |

### Advanced Options (Optional)

| Variable | Description | Default |
|-------------------------------|-----------------------------------------------------------------------|---------------|
| `COMMAND_WAIT_DELAY` | Delay in seconds between checking for command status | 1 |
| `COMMAND_WAIT_ATTEMPTS` | Number of attempts to check for command completeion before giving up | 600 |
| `COMMAND_WAIT_ATTEMPTS` | Number of attempts to check for command completion before giving up | 600 |
| `MINIMUM_DOWNLOAD_QUEUE_SIZE` | Minimum number of items in the download queue before starting a hunt | -1 |

### Detailed Configuration Explanation
Expand Down Expand Up @@ -149,6 +162,11 @@ The following environment variables can be configured:
- When set to `true`, the script will output detailed debugging information about API responses and internal operations.
- Useful for troubleshooting issues but can make logs verbose.

- **ENABLE_WEB_UI**
- When set to `true`, the web interface will be enabled on port 8988.
- When set to `false`, the web interface will not start, saving resources.
- Default is `true` for convenient monitoring.

- **COMMAND_WAIT_DELAY**
- Certain operations like refreshing and searching happen asynchronously.
- This is the delay in seconds between checking the status of these operations for completion.
Expand All @@ -163,6 +181,53 @@ The following environment variables can be configured:
- This helps prevent overwhelming the queue with too many download requests at once and avoids creating a massive backlog of downloads.
- Set to `-1` to disable this check.

## Web Interface

Huntarr-Sonarr includes a real-time log viewer web interface that allows you to monitor its operation directly from your browser.

### Features

- **Real-time Log Updates**: Logs refresh automatically every second
- **Day/Night Mode**: Toggle between light and dark themes
- **Color-coded Log Entries**: Different log levels are displayed in different colors
- **Auto-scrolling**: Automatically scrolls to the latest log entries
- **Connection Status**: Shows whether the connection to the log stream is active

### How to Access

The web interface is available on port 8988. Simply navigate to:

```
http://YOUR_SERVER_IP:8988
```

Or if you're accessing it locally:

```
http://localhost:8988
```

### Port Configuration Explained

When running with Docker, you need to map the container's internal port to a port on your host system. The format is `HOST_PORT:CONTAINER_PORT`.

For example:
- `8988:8988` means "map port 8988 from the host to port 8988 in the container"

If you want to use a different port on your host (e.g., 9000), you would use:
- `9000:8988` means "map port 9000 from the host to port 8988 in the container"

You would then access the web interface at `http://YOUR_SERVER_IP:9000`

### Enabling/Disabling the Web UI

The web interface can be enabled or disabled using the `ENABLE_WEB_UI` environment variable:

- `ENABLE_WEB_UI=true` - Enable the web interface (default)
- `ENABLE_WEB_UI=false` - Disable the web interface

If you disable the web interface, you don't need to expose the port in your Docker configuration.

---

## Installation Methods
Expand All @@ -174,6 +239,7 @@ The simplest way to run Huntarr is via Docker:
```bash
docker run -d --name huntarr-sonarr \
--restart always \
-p 8988:8988 \ # Can be removed if ENABLE_WEB_UI=false
-e API_KEY="your-api-key" \
-e API_URL="http://your-sonarr-address:8989" \
-e MONITORED_ONLY="true" \
Expand All @@ -183,10 +249,16 @@ docker run -d --name huntarr-sonarr \
-e RANDOM_SELECTION="true" \
-e STATE_RESET_INTERVAL_HOURS="168" \
-e DEBUG_MODE="false" \
-e ENABLE_WEB_UI="true" \
huntarr/4sonarr:latest

# Optional advanced settings
# -e COMMAND_WAIT_DELAY="1" \
# -e COMMAND_WAIT_ATTEMPTS="600" \
# -e MINIMUM_DOWNLOAD_QUEUE_SIZE="-1" \
```

To check on the status of the program, you should see new files downloading or you can type:
To check on the status of the program, you can use the web interface at http://YOUR_SERVER_IP:8988 or check the logs with:
```bash
docker logs huntarr-sonarr
```
Expand All @@ -202,6 +274,8 @@ services:
image: huntarr/4sonarr:latest
container_name: huntarr-sonarr
restart: always
ports:
- "8988:8988" # Can be removed if ENABLE_WEB_UI=false
environment:
API_KEY: "your-api-key"
API_URL: "http://your-sonarr-address:8989"
Expand All @@ -213,6 +287,12 @@ services:
RANDOM_SELECTION: "true"
STATE_RESET_INTERVAL_HOURS: "168"
DEBUG_MODE: "false"
ENABLE_WEB_UI: "true"

# Optional advanced settings
# COMMAND_WAIT_DELAY: "1"
# COMMAND_WAIT_ATTEMPTS: "600"
# MINIMUM_DOWNLOAD_QUEUE_SIZE: "-1"
```

Then run:
Expand All @@ -228,6 +308,7 @@ Run this from Command Line in Unraid:
```bash
docker run -d --name huntarr-sonarr \
--restart always \
-p 8988:8988 \ # Can be removed if ENABLE_WEB_UI=false
-e API_KEY="your-api-key" \
-e API_URL="http://your-sonarr-address:8989" \
-e API_TIMEOUT="60" \
Expand All @@ -238,7 +319,13 @@ docker run -d --name huntarr-sonarr \
-e RANDOM_SELECTION="true" \
-e STATE_RESET_INTERVAL_HOURS="168" \
-e DEBUG_MODE="false" \
-e ENABLE_WEB_UI="true" \
huntarr/4sonarr:latest

# Optional advanced settings
# -e COMMAND_WAIT_DELAY="1" \
# -e COMMAND_WAIT_ATTEMPTS="600" \
# -e MINIMUM_DOWNLOAD_QUEUE_SIZE="-1" \
```

### SystemD Service
Expand Down Expand Up @@ -289,6 +376,7 @@ sudo systemctl start huntarr
- **New Show Setup**: Automatically find episodes for newly added shows
- **Background Service**: Run it in the background to continuously maintain your library
- **Smart Rotation**: With state tracking, ensures all content gets attention over time
- **Real-time Monitoring**: Use the web interface to see what's happening at any time

## Tips

Expand All @@ -297,40 +385,27 @@ sudo systemctl start huntarr
- **Batch Size Control**: Adjust `HUNT_MISSING_SHOWS` and `HUNT_UPGRADE_EPISODES` based on your indexer's rate limits
- **Monitored Status**: Set `MONITORED_ONLY=false` if you want to download all missing episodes regardless of monitored status
- **System Resources**: The script uses minimal resources and can run continuously on even low-powered systems
- **Web Interface**: Use the web interface to monitor progress instead of checking Docker logs
- **Port Conflicts**: If port 8988 is already in use, map to a different host port (e.g., `-p 9000:8988`)
- **Disable Web UI**: Set `ENABLE_WEB_UI=false` if you don't need the interface to save resources
- **Debugging Issues**: Enable `DEBUG_MODE=true` temporarily to see detailed logs when troubleshooting

## Troubleshooting

- **API Key Issues**: Check that your API key is correct in Sonarr settings
- **Connection Problems**: Ensure the Sonarr URL is accessible from where you're running the script
- **Command Failures**: If search commands fail, try using the Sonarr UI to verify what commands are available in your version
- **Web Interface Not Loading**: Make sure port 8988 is exposed in your Docker configuration and not blocked by a firewall
- **Logs**: Check the container logs with `docker logs huntarr-sonarr` if running in Docker
- **Debug Mode**: Enable `DEBUG_MODE=true` to see detailed API responses and process flow
- **State Files**: The script stores state in `/tmp/huntarr-state/` - if something seems stuck, you can try deleting these files

---

**Change Log:**
- **v1**: Original code written
- **v2**: Optimized search
- **v3**: Variable names changed for docker optimization
- **v4**: Added monitored only tag
- **v5**: Added quality upgrade functionality to find episodes below cutoff quality
- **v6**: Added state tracking to prevent duplicate searches
- **v7**: Implemented configurable state reset timer
- **v8**: Added debug mode and improved error handling
- **v9**: Enhanced random selection mode for better distribution
- **v10**: Renamed from "Sonarr Hunter" to "Huntarr"
- **v11**: Complete modular refactoring for better maintainability
- **v12**: Improved variable naming with HUNT_ prefix
- **v13**: Enhanced state management and cycle processing

---

This script helps automate the tedious process of finding missing episodes and quality upgrades in your TV collection, running quietly in the background while respecting your indexers' rate limits.

---

Thanks to:

[IntensiveCareCub](https://www.reddit.com/user/IntensiveCareCub/) for the Hunter to Huntarr idea!
[IntensiveCareCub](https://www.reddit.com/user/IntensiveCareCub/) for the Hunter to Huntarr idea!
4 changes: 4 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import os
import logging

# Web UI Configuration
ENABLE_WEB_UI = os.environ.get("ENABLE_WEB_UI", "true").lower() == "true"

# API Configuration
API_KEY = os.environ.get("API_KEY", "your-api-key")
API_URL = os.environ.get("API_URL", "http://your-sonarr-address:8989")
Expand Down Expand Up @@ -89,4 +92,5 @@ def log_configuration(logger):
logger.info(f"MONITORED_ONLY={MONITORED_ONLY}, RANDOM_SELECTION={RANDOM_SELECTION}")
logger.info(f"HUNT_MODE={HUNT_MODE}, SLEEP_DURATION={SLEEP_DURATION}s")
logger.info(f"COMMAND_WAIT_DELAY={COMMAND_WAIT_DELAY}, COMMAND_WAIT_ATTEMPTS={COMMAND_WAIT_ATTEMPTS}")
logger.info(f"ENABLE_WEB_UI={ENABLE_WEB_UI}")
logger.debug(f"API_KEY={API_KEY}")
47 changes: 44 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,39 @@

import time
import sys
import os
import socket
from utils.logger import logger
from config import HUNT_MODE, SLEEP_DURATION, MINIMUM_DOWNLOAD_QUEUE_SIZE, log_configuration
from config import HUNT_MODE, SLEEP_DURATION, MINIMUM_DOWNLOAD_QUEUE_SIZE, ENABLE_WEB_UI, log_configuration
from missing import process_missing_episodes
from upgrade import process_cutoff_upgrades
from state import check_state_reset, calculate_reset_time
from api import get_download_queue_size

def get_ip_address():
"""Get the host's IP address or hostname for display"""
try:
# Try to get the container's hostname
hostname = socket.gethostname()
# Try to get the container's IP
ip = socket.gethostbyname(hostname)
return ip
except:
return "YOUR_SERVER_IP"

def main_loop() -> None:
"""Main processing loop for Huntarr-Sonarr"""

# Log welcome message for web interface
logger.info("=== Huntarr [Sonarr Edition] Starting ===")

# Log web UI information if enabled
if ENABLE_WEB_UI:
server_ip = get_ip_address()
logger.info(f"Web interface available at http://{server_ip}:8988")

logger.info("GitHub: https://github.com/plexguide/huntarr-sonarr")

while True:
# Check if state files need to be reset
check_state_reset()
Expand All @@ -38,15 +62,32 @@ def main_loop() -> None:
processing_done = True

else:
logger.info(f"Download queue size ({download_queue_size}) is above the minimum threshold ({MINIMUM_DOWNLOAD_QUEUE_SIZE}). Skipped processing.")
logger.info(f"Download queue size ({download_queue_size}) is above the minimum threshold ({MINIMUM_DOWNLOAD_QUEUE_SIZE}). Skipped processing.")

# Calculate time until the next reset
calculate_reset_time()

# Sleep at the end of the cycle only
logger.info(f"Cycle complete. Sleeping {SLEEP_DURATION}s before next cycle...")
logger.info("⭐ Tool Great? Donate @ https://donate.plex.one for Daughter's College Fund!")
time.sleep(SLEEP_DURATION)

# Log web UI information if enabled
if ENABLE_WEB_UI:
server_ip = get_ip_address()
logger.info(f"Web interface available at http://{server_ip}:8988")

# Sleep with progress updates for the web interface
sleep_start = time.time()
sleep_end = sleep_start + SLEEP_DURATION

while time.time() < sleep_end:
# Sleep in smaller chunks for more responsive shutdown
time.sleep(min(10, sleep_end - time.time()))

# Every minute, log the remaining sleep time for web interface visibility
if int((time.time() - sleep_start) % 60) == 0 and time.time() < sleep_end - 10:
remaining = int(sleep_end - time.time())
logger.debug(f"Sleeping... {remaining}s remaining until next cycle")

if __name__ == "__main__":
# Log configuration settings
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
requests>=2.25.0
requests>=2.25.0
flask>=2.0.0
Loading