diff --git a/Dockerfile b/Dockerfile index c2e402ab..e98dcd03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" \ @@ -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"] \ No newline at end of file + 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"] \ No newline at end of file diff --git a/README.md b/README.md index ffe3ac8d..a640ba77 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 @@ -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 @@ -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. @@ -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 @@ -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" \ @@ -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 ``` @@ -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" @@ -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: @@ -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" \ @@ -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 @@ -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 @@ -297,6 +385,9 @@ 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 @@ -304,33 +395,17 @@ sudo systemctl start huntarr - **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! \ No newline at end of file diff --git a/config.py b/config.py index fd2ad61b..2cf8e5f1 100644 --- a/config.py +++ b/config.py @@ -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") @@ -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}") \ No newline at end of file diff --git a/main.py b/main.py index 636f59f0..7545ec49 100644 --- a/main.py +++ b/main.py @@ -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() @@ -38,7 +62,7 @@ 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() @@ -46,7 +70,24 @@ def main_loop() -> None: # 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 diff --git a/requirements.txt b/requirements.txt index eed69883..730695f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -requests>=2.25.0 \ No newline at end of file +requests>=2.25.0 +flask>=2.0.0 \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 00000000..4da6ea4b --- /dev/null +++ b/start.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Startup script for Huntarr-Sonarr that conditionally starts the web UI + +# Convert to lowercase +ENABLE_WEB_UI=$(echo "${ENABLE_WEB_UI:-true}" | tr '[:upper:]' '[:lower:]') + +if [ "$ENABLE_WEB_UI" = "true" ]; then + echo "Starting with Web UI enabled on port 8988" + # Start both the web server and the main application + python web_server.py & + python main.py +else + echo "Web UI disabled, starting only the main application" + # Start only the main application + python main.py +fi \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 00000000..4d332297 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,318 @@ + + +
+ + +Visit the Huntarr [Sonarr Edition] GITHUB Page and click the ⭐ in the Upper Right!
+