From 281f03835dc4fd04b3b9788bf6b28a034994804e Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Mon, 7 Apr 2025 13:42:24 -0400
Subject: [PATCH 01/12] update
---
Dockerfile | 23 ++-
main.py | 24 +++-
requirements.txt | 3 +-
setup.py | 27 ++++
templates/index.html | 134 ++++++++++++++++++
.../.github}/workflows/docker-image.yml | 0
utils/logger.py | 14 +-
webserver.py | 0
8 files changed, 218 insertions(+), 7 deletions(-)
create mode 100644 setup.py
create mode 100644 templates/index.html
rename {.github => utils/.github}/workflows/docker-image.yml (100%)
create mode 100644 webserver.py
diff --git a/Dockerfile b/Dockerfile
index c2e402ab..01493e1c 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" \
@@ -20,5 +33,9 @@ ENV API_KEY="your-api-key" \
RANDOM_SELECTION="true" \
MONITORED_ONLY="true" \
DEBUG_MODE="false"
-# Run the application
-CMD ["python", "main.py"]
\ No newline at end of file
+
+# Expose web interface port
+EXPOSE 8988
+
+# Run the application with web interface
+CMD ["sh", "-c", "python web_server.py & python main.py"]
\ No newline at end of file
diff --git a/main.py b/main.py
index 636f59f0..15bd95a0 100644
--- a/main.py
+++ b/main.py
@@ -6,6 +6,7 @@
import time
import sys
+import os
from utils.logger import logger
from config import HUNT_MODE, SLEEP_DURATION, MINIMUM_DOWNLOAD_QUEUE_SIZE, log_configuration
from missing import process_missing_episodes
@@ -15,6 +16,12 @@
def main_loop() -> None:
"""Main processing loop for Huntarr-Sonarr"""
+
+ # Log welcome message for web interface
+ logger.info("=== Huntarr [Sonarr Edition] Starting ===")
+ logger.info("Web interface available at http://YOUR_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 +45,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 +53,20 @@ 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)
+ logger.info("Web interface available at http://YOUR_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/setup.py b/setup.py
new file mode 100644
index 00000000..7cb23d34
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Setup script for Huntarr-Sonarr Web Interface
+
+# Create directories
+mkdir -p templates
+mkdir -p utils
+
+# Ensure file destinations exist
+touch requirements.txt
+touch main.py
+touch config.py
+touch api.py
+touch missing.py
+touch upgrade.py
+touch state.py
+touch web_server.py
+touch utils/logger.py
+touch utils/__init__.py
+touch templates/index.html
+
+echo "Directory structure created successfully!"
+echo "Please copy the files from the artifacts into the appropriate locations."
+echo "Then build the Docker image with: docker build -t huntarr/4sonarr:latest ."
+
+# Reminder about port exposure
+echo "Remember to expose port 8988 when running the Docker container:"
+echo "docker run -d --name huntarr-sonarr -p 8988:8988 ... huntarr/4sonarr:latest"
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 00000000..122df930
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,134 @@
+
+
+
+
+
+ Huntarr-Sonarr Log Viewer
+
+
+
+
+
+ Connected: Yes | Auto-refresh: Every 1 second
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.github/workflows/docker-image.yml b/utils/.github/workflows/docker-image.yml
similarity index 100%
rename from .github/workflows/docker-image.yml
rename to utils/.github/workflows/docker-image.yml
diff --git a/utils/logger.py b/utils/logger.py
index 05128a62..7a1150c5 100644
--- a/utils/logger.py
+++ b/utils/logger.py
@@ -6,8 +6,14 @@
import logging
import sys
import os
+import pathlib
from config import DEBUG_MODE
+# Create log directory
+LOG_DIR = pathlib.Path("/tmp/huntarr-logs")
+LOG_DIR.mkdir(parents=True, exist_ok=True)
+LOG_FILE = LOG_DIR / "huntarr.log"
+
def setup_logger():
"""Configure and return the application logger"""
logger = logging.getLogger("huntarr-sonarr")
@@ -19,15 +25,21 @@ def setup_logger():
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG if DEBUG_MODE else logging.INFO)
+ # Create file handler for the web interface
+ file_handler = logging.FileHandler(LOG_FILE)
+ file_handler.setLevel(logging.DEBUG if DEBUG_MODE else logging.INFO)
+
# Set format
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
console_handler.setFormatter(formatter)
+ file_handler.setFormatter(formatter)
- # Add handler to logger
+ # Add handlers to logger
logger.addHandler(console_handler)
+ logger.addHandler(file_handler)
return logger
diff --git a/webserver.py b/webserver.py
new file mode 100644
index 00000000..e69de29b
From 5fb7b67d62864ed4873673a5e4dfc3fb6ec4302b Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Mon, 7 Apr 2025 13:43:09 -0400
Subject: [PATCH 02/12] update
---
setup.py | 27 ---------------------------
1 file changed, 27 deletions(-)
delete mode 100644 setup.py
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 7cb23d34..00000000
--- a/setup.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-# Setup script for Huntarr-Sonarr Web Interface
-
-# Create directories
-mkdir -p templates
-mkdir -p utils
-
-# Ensure file destinations exist
-touch requirements.txt
-touch main.py
-touch config.py
-touch api.py
-touch missing.py
-touch upgrade.py
-touch state.py
-touch web_server.py
-touch utils/logger.py
-touch utils/__init__.py
-touch templates/index.html
-
-echo "Directory structure created successfully!"
-echo "Please copy the files from the artifacts into the appropriate locations."
-echo "Then build the Docker image with: docker build -t huntarr/4sonarr:latest ."
-
-# Reminder about port exposure
-echo "Remember to expose port 8988 when running the Docker container:"
-echo "docker run -d --name huntarr-sonarr -p 8988:8988 ... huntarr/4sonarr:latest"
\ No newline at end of file
From 52842cefdb6b9100965f1de7e82d413454699d26 Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Mon, 7 Apr 2025 13:46:03 -0400
Subject: [PATCH 03/12] update
---
.github/workflows/docker-image.yml | 93 ++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 .github/workflows/docker-image.yml
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
new file mode 100644
index 00000000..ea7c086d
--- /dev/null
+++ b/.github/workflows/docker-image.yml
@@ -0,0 +1,93 @@
+name: Docker Build and Push
+on:
+ push:
+ branches:
+ - main
+ - dev
+ tags:
+ - "*" # This will trigger on any tag push
+ pull_request:
+ branches:
+ - main
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ steps:
+ # 1) Check out your repository code with full depth
+ - name: Checkout code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ # 2) List files to verify huntarr.py is present
+ - name: List files in directory
+ run: ls -la
+
+ # 3) Set up QEMU for multi-architecture builds
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ with:
+ platforms: arm64,amd64
+
+ # 4) Set up Docker Buildx
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ # 5) Log in to Docker Hub
+ - name: Log in to Docker Hub
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_PASSWORD }}
+
+ # 6) Extract version from tag if it's a tag push
+ - name: Extract version from tag
+ if: startsWith(github.ref, 'refs/tags/')
+ id: get_version
+ run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
+
+ # 7a) Build & Push if on 'main' branch
+ - name: Build and Push (main)
+ if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ huntarr/4sonarr:latest
+ huntarr/4sonarr:${{ github.sha }}
+
+ # 7b) Build & Push if on 'dev' branch
+ - name: Build and Push (dev)
+ if: github.ref == 'refs/heads/dev' && github.event_name != 'pull_request'
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ huntarr/4sonarr:dev
+ huntarr/4sonarr:${{ github.sha }}
+
+ # 7c) Build & Push if it's a tag/release
+ - name: Build and Push (release)
+ if: startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request'
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ platforms: linux/amd64,linux/arm64
+ tags: |
+ huntarr/4sonarr:${{ steps.get_version.outputs.VERSION }}
+ huntarr/4sonarr:latest
+
+ # 7d) Just build on pull requests
+ - name: Build (PR)
+ if: github.event_name == 'pull_request'
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: false
+ platforms: linux/amd64,linux/arm64
\ No newline at end of file
From be90b6b717371e389507c18d382d5f683bba67d5 Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Mon, 7 Apr 2025 13:51:36 -0400
Subject: [PATCH 04/12] update
---
web_server.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
webserver.py | 0
2 files changed, 64 insertions(+)
create mode 100644 web_server.py
delete mode 100644 webserver.py
diff --git a/web_server.py b/web_server.py
new file mode 100644
index 00000000..1c032115
--- /dev/null
+++ b/web_server.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""
+Web server for Huntarr-Sonarr
+Provides a web interface to view logs in real-time
+"""
+
+import os
+import time
+import datetime
+import pathlib
+from flask import Flask, render_template, Response, stream_with_context
+import logging
+
+# Disable Flask default logging
+log = logging.getLogger('werkzeug')
+log.setLevel(logging.ERROR)
+
+# Create Flask app
+app = Flask(__name__)
+
+# Log file location
+LOG_FILE = "/tmp/huntarr-logs/huntarr.log"
+LOG_DIR = pathlib.Path("/tmp/huntarr-logs")
+LOG_DIR.mkdir(parents=True, exist_ok=True)
+
+@app.route('/')
+def index():
+ """Render the main page"""
+ return render_template('index.html')
+
+@app.route('/logs')
+def stream_logs():
+ """Stream logs to the client"""
+ def generate():
+ # First get all existing logs
+ if os.path.exists(LOG_FILE):
+ with open(LOG_FILE, 'r') as f:
+ # Read the last 100 lines of the log file
+ lines = f.readlines()[-100:]
+ for line in lines:
+ yield f"data: {line}\n\n"
+
+ # Then stream new logs as they appear
+ with open(LOG_FILE, 'r') as f:
+ # Move to the end of the file
+ f.seek(0, 2)
+ while True:
+ line = f.readline()
+ if line:
+ yield f"data: {line}\n\n"
+ else:
+ time.sleep(0.1)
+
+ return Response(stream_with_context(generate()),
+ mimetype='text/event-stream')
+
+if __name__ == "__main__":
+ # Create a basic log entry at startup
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ with open(LOG_FILE, 'a') as f:
+ f.write(f"{timestamp} - huntarr-web - INFO - Web server starting on port 8988\n")
+
+ # Run the Flask app
+ app.run(host='0.0.0.0', port=8988, debug=False, threaded=True)
\ No newline at end of file
diff --git a/webserver.py b/webserver.py
deleted file mode 100644
index e69de29b..00000000
From 4f366ea29bf1d4716917421ecf736b73f06785be Mon Sep 17 00:00:00 2001
From: Admin9705 <9705@duck.com>
Date: Mon, 7 Apr 2025 14:18:47 -0400
Subject: [PATCH 05/12] update
---
templates/index.html | 168 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 151 insertions(+), 17 deletions(-)
diff --git a/templates/index.html b/templates/index.html
index 122df930..ed81d699 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,73 +1,194 @@
-
+
Huntarr-Sonarr Log Viewer
- Connected: Yes | Auto-refresh: Every 1 second
+
+
+
+
Connected: Yes | Auto-refresh: Every 1 second
+
+
+ Night Mode
+
+
+