mirror of
https://github.com/plexguide/Huntarr.git
synced 2026-01-06 11:10:11 -06:00
Refactor Requestor module to Requestarr and update related functionality
- Removed the Requestor module and replaced it with the Requestarr module, including all associated files and functionality. - Updated frontend templates and JavaScript to reflect the new Requestarr naming convention. - Adjusted database schema to create new tables for Requestarr settings and requests. - Enhanced the web server and blueprints to register the new Requestarr routes and API endpoints. - Improved navigation and UI elements to support the transition from Requestor to Requestarr.
This commit is contained in:
@@ -602,6 +602,17 @@ let huntarrUI = {
|
||||
this.currentSection = 'scheduling';
|
||||
|
||||
console.debug('Scheduling section activated');
|
||||
} else if (section === 'requestarr' && document.getElementById('requestarr-section')) {
|
||||
document.getElementById('requestarr-section').classList.add('active');
|
||||
document.getElementById('requestarr-section').style.display = 'block';
|
||||
if (document.getElementById('requestarrNav')) document.getElementById('requestarrNav').classList.add('active');
|
||||
newTitle = 'Requestarr';
|
||||
this.currentSection = 'requestarr';
|
||||
|
||||
// Initialize requestarr module if it exists
|
||||
if (typeof window.requestarrModule !== 'undefined') {
|
||||
window.requestarrModule.loadInstances();
|
||||
}
|
||||
} else {
|
||||
// Default to home if section is unknown or element missing
|
||||
if (this.elements.homeSection) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Requestor functionality - Media search and request system
|
||||
* Requestarr functionality - Media search and request system
|
||||
*/
|
||||
|
||||
class RequestorModule {
|
||||
class RequestarrModule {
|
||||
constructor() {
|
||||
this.searchTimeout = null;
|
||||
this.instances = { sonarr: [], radarr: [] };
|
||||
@@ -17,13 +17,13 @@ class RequestorModule {
|
||||
|
||||
setupEventListeners() {
|
||||
// Instance selection
|
||||
const instanceSelect = document.getElementById('requestor-instance-select');
|
||||
const instanceSelect = document.getElementById('requestarr-instance-select');
|
||||
if (instanceSelect) {
|
||||
instanceSelect.addEventListener('change', (e) => this.handleInstanceChange(e));
|
||||
}
|
||||
|
||||
// Search input with debouncing
|
||||
const searchInput = document.getElementById('requestor-search');
|
||||
const searchInput = document.getElementById('requestarr-search');
|
||||
if (searchInput) {
|
||||
searchInput.disabled = true;
|
||||
searchInput.placeholder = 'Select an instance first...';
|
||||
@@ -56,7 +56,7 @@ class RequestorModule {
|
||||
|
||||
// Clear previous results and enable search
|
||||
this.clearResults();
|
||||
const searchInput = document.getElementById('requestor-search');
|
||||
const searchInput = document.getElementById('requestarr-search');
|
||||
if (searchInput) {
|
||||
searchInput.disabled = false;
|
||||
searchInput.placeholder = `Search for ${appType === 'radarr' ? 'movies' : 'TV shows'}...`;
|
||||
@@ -64,7 +64,7 @@ class RequestorModule {
|
||||
}
|
||||
} else {
|
||||
this.selectedInstance = null;
|
||||
const searchInput = document.getElementById('requestor-search');
|
||||
const searchInput = document.getElementById('requestarr-search');
|
||||
if (searchInput) {
|
||||
searchInput.disabled = true;
|
||||
searchInput.placeholder = 'Select an instance first...';
|
||||
@@ -76,7 +76,7 @@ class RequestorModule {
|
||||
|
||||
async loadInstances() {
|
||||
try {
|
||||
const response = await fetch('./api/requestor/instances');
|
||||
const response = await fetch('./api/requestarr/instances');
|
||||
this.instances = await response.json();
|
||||
this.updateInstanceSelect();
|
||||
} catch (error) {
|
||||
@@ -86,7 +86,7 @@ class RequestorModule {
|
||||
}
|
||||
|
||||
updateInstanceSelect() {
|
||||
const instanceSelect = document.getElementById('requestor-instance-select');
|
||||
const instanceSelect = document.getElementById('requestarr-instance-select');
|
||||
if (!instanceSelect) return;
|
||||
|
||||
instanceSelect.innerHTML = '<option value="">Select an instance to search...</option>';
|
||||
@@ -114,7 +114,7 @@ class RequestorModule {
|
||||
return;
|
||||
}
|
||||
|
||||
const resultsContainer = document.getElementById('requestor-results');
|
||||
const resultsContainer = document.getElementById('requestarr-results');
|
||||
if (!resultsContainer) return;
|
||||
|
||||
// Show loading
|
||||
@@ -127,7 +127,7 @@ class RequestorModule {
|
||||
instance_name: this.selectedInstance.instanceName
|
||||
});
|
||||
|
||||
const response = await fetch(`./api/requestor/search?${params}`);
|
||||
const response = await fetch(`./api/requestarr/search?${params}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
@@ -143,7 +143,7 @@ class RequestorModule {
|
||||
}
|
||||
|
||||
displayResults(results) {
|
||||
const resultsContainer = document.getElementById('requestor-results');
|
||||
const resultsContainer = document.getElementById('requestarr-results');
|
||||
if (!resultsContainer) return;
|
||||
|
||||
if (results.length === 0) {
|
||||
@@ -283,7 +283,7 @@ class RequestorModule {
|
||||
instance_name: this.selectedInstance.instanceName
|
||||
};
|
||||
|
||||
const response = await fetch('./api/requestor/request', {
|
||||
const response = await fetch('./api/requestarr/request', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -320,7 +320,7 @@ class RequestorModule {
|
||||
}
|
||||
|
||||
clearResults() {
|
||||
const resultsContainer = document.getElementById('requestor-results');
|
||||
const resultsContainer = document.getElementById('requestarr-results');
|
||||
if (resultsContainer) {
|
||||
resultsContainer.innerHTML = '';
|
||||
}
|
||||
@@ -346,7 +346,7 @@ class RequestorModule {
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (document.getElementById('requestor-section')) {
|
||||
window.requestorModule = new RequestorModule();
|
||||
if (document.getElementById('requestarr-section')) {
|
||||
window.requestarrModule = new RequestarrModule();
|
||||
}
|
||||
});
|
||||
@@ -60,11 +60,11 @@
|
||||
</div>
|
||||
<span>Hunt Manager</span>
|
||||
</a>
|
||||
<a href="./#requestor" class="nav-item" id="requestorNav">
|
||||
<a href="./#requestarr" class="nav-item" id="requestarrNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
</div>
|
||||
<span>Requestor</span>
|
||||
<span>Requestarr</span>
|
||||
</a>
|
||||
<a href="./user" class="nav-item" id="userNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
{% include 'components/hunt_manager_section.html' %}
|
||||
|
||||
<!-- Requestor Section -->
|
||||
{% include 'components/requestor_section.html' %}
|
||||
{% include 'components/requestarr_section.html' %}
|
||||
|
||||
<!-- Apps Section -->
|
||||
{% include 'components/apps_section.html' %}
|
||||
|
||||
@@ -12,7 +12,7 @@ from src.primary.apps.readarr_routes import readarr_bp
|
||||
from src.primary.apps.whisparr_routes import whisparr_bp
|
||||
from src.primary.apps.eros_routes import eros_bp
|
||||
from src.primary.apps.swaparr_routes import swaparr_bp
|
||||
from src.primary.apps.requestor_routes import requestor_bp
|
||||
from src.primary.apps.requestarr_routes import requestarr_bp
|
||||
|
||||
__all__ = [
|
||||
"sonarr_bp",
|
||||
@@ -22,5 +22,5 @@ __all__ = [
|
||||
"whisparr_bp",
|
||||
"eros_bp",
|
||||
"swaparr_bp",
|
||||
"requestor_bp"
|
||||
"requestarr_bp"
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Requestor module for searching and requesting media through TMDB and *arr apps
|
||||
Requestarr module for searching and requesting media through TMDB and *arr apps
|
||||
"""
|
||||
|
||||
import requests
|
||||
@@ -9,8 +9,8 @@ from src.primary.utils.database import get_database
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class RequestorAPI:
|
||||
"""API handler for Requestor functionality"""
|
||||
class RequestarrAPI:
|
||||
"""API handler for Requestarr functionality"""
|
||||
|
||||
def __init__(self):
|
||||
self.db = get_database()
|
||||
@@ -687,4 +687,4 @@ class RequestorAPI:
|
||||
}
|
||||
|
||||
# Global instance
|
||||
requestor_api = RequestorAPI()
|
||||
requestarr_api = RequestarrAPI()
|
||||
@@ -1,17 +1,17 @@
|
||||
"""
|
||||
Requestor routes for media search and request functionality
|
||||
Requestarr routes for media search and request functionality
|
||||
"""
|
||||
|
||||
from flask import Blueprint, request, jsonify
|
||||
import logging
|
||||
from src.primary.apps.requestor import requestor_api
|
||||
from src.primary.apps.requestarr import requestarr_api
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Create blueprint
|
||||
requestor_bp = Blueprint('requestor', __name__, url_prefix='/api/requestor')
|
||||
requestarr_bp = Blueprint('requestarr', __name__, url_prefix='/api/requestarr')
|
||||
|
||||
@requestor_bp.route('/search', methods=['GET'])
|
||||
@requestarr_bp.route('/search', methods=['GET'])
|
||||
def search_media():
|
||||
"""Search for media using TMDB with availability checking"""
|
||||
try:
|
||||
@@ -25,24 +25,24 @@ def search_media():
|
||||
if not app_type or not instance_name:
|
||||
return jsonify({'error': 'App type and instance name are required'}), 400
|
||||
|
||||
results = requestor_api.search_media_with_availability(query, app_type, instance_name)
|
||||
results = requestarr_api.search_media_with_availability(query, app_type, instance_name)
|
||||
return jsonify({'results': results})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error searching media: {e}")
|
||||
return jsonify({'error': 'Search failed'}), 500
|
||||
|
||||
@requestor_bp.route('/instances', methods=['GET'])
|
||||
@requestarr_bp.route('/instances', methods=['GET'])
|
||||
def get_enabled_instances():
|
||||
"""Get enabled Sonarr and Radarr instances"""
|
||||
try:
|
||||
instances = requestor_api.get_enabled_instances()
|
||||
instances = requestarr_api.get_enabled_instances()
|
||||
return jsonify(instances)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting instances: {e}")
|
||||
return jsonify({'error': 'Failed to get instances'}), 500
|
||||
|
||||
@requestor_bp.route('/request', methods=['POST'])
|
||||
@requestarr_bp.route('/request', methods=['POST'])
|
||||
def request_media():
|
||||
"""Request media through app instance"""
|
||||
try:
|
||||
@@ -54,7 +54,7 @@ def request_media():
|
||||
if field not in data:
|
||||
return jsonify({'success': False, 'error': f'Missing required field: {field}'}), 400
|
||||
|
||||
result = requestor_api.request_media(
|
||||
result = requestarr_api.request_media(
|
||||
tmdb_id=data['tmdb_id'],
|
||||
media_type=data['media_type'],
|
||||
title=data['title'],
|
||||
@@ -75,19 +75,19 @@ def request_media():
|
||||
logger.error(f"Error requesting media: {e}")
|
||||
return jsonify({'success': False, 'error': 'Request failed'}), 500
|
||||
|
||||
@requestor_bp.route('/requests', methods=['GET'])
|
||||
@requestarr_bp.route('/requests', methods=['GET'])
|
||||
def get_requests():
|
||||
"""Get paginated list of requests"""
|
||||
try:
|
||||
page = int(request.args.get('page', 1))
|
||||
page_size = int(request.args.get('page_size', 20))
|
||||
|
||||
requests_data = requestor_api.db.get_requests(page, page_size)
|
||||
requests_data = requestarr_api.db.get_requests(page, page_size)
|
||||
return jsonify(requests_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting requests: {e}")
|
||||
return jsonify({'error': 'Failed to get requests'}), 500
|
||||
|
||||
# Requestor is always enabled with hardcoded TMDB API key
|
||||
logger.info("Requestor initialized with hardcoded TMDB API key")
|
||||
# Requestarr is always enabled with hardcoded TMDB API key
|
||||
logger.info("Requestarr initialized with hardcoded TMDB API key")
|
||||
@@ -742,9 +742,9 @@ class HuntarrDatabase:
|
||||
# Logs table moved to separate logs.db - remove if it exists
|
||||
conn.execute('DROP TABLE IF EXISTS logs')
|
||||
|
||||
# Create requestor_settings table for Requestor configuration
|
||||
# Create requestarr_settings table for Requestarr configuration
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS requestor_settings (
|
||||
CREATE TABLE IF NOT EXISTS requestarr_settings (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
tmdb_api_key TEXT,
|
||||
enabled BOOLEAN DEFAULT FALSE,
|
||||
@@ -753,9 +753,9 @@ class HuntarrDatabase:
|
||||
)
|
||||
''')
|
||||
|
||||
# Create requestor_requests table for tracking media requests
|
||||
# Create requestarr_requests table for tracking media requests
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS requestor_requests (
|
||||
CREATE TABLE IF NOT EXISTS requestarr_requests (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tmdb_id INTEGER NOT NULL,
|
||||
media_type TEXT NOT NULL CHECK (media_type IN ('movie', 'tv')),
|
||||
@@ -2313,13 +2313,13 @@ class HuntarrDatabase:
|
||||
logger.error(f"Failed to check setup progress: {e}")
|
||||
return False
|
||||
|
||||
# Requestor functionality methods
|
||||
def get_requestor_settings(self) -> Dict[str, Any]:
|
||||
"""Get Requestor configuration settings"""
|
||||
# Requestarr functionality methods
|
||||
def get_requestarr_settings(self) -> Dict[str, Any]:
|
||||
"""Get Requestarr configuration settings"""
|
||||
try:
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.execute("""
|
||||
SELECT tmdb_api_key, enabled FROM requestor_settings WHERE id = 1
|
||||
SELECT tmdb_api_key, enabled FROM requestarr_settings WHERE id = 1
|
||||
""")
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
@@ -2329,20 +2329,20 @@ class HuntarrDatabase:
|
||||
}
|
||||
return {'tmdb_api_key': '', 'enabled': False}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting requestor settings: {e}")
|
||||
logger.error(f"Error getting requestarr settings: {e}")
|
||||
return {'tmdb_api_key': '', 'enabled': False}
|
||||
|
||||
def save_requestor_settings(self, tmdb_api_key: str, enabled: bool) -> bool:
|
||||
"""Save Requestor configuration settings"""
|
||||
def save_requestarr_settings(self, tmdb_api_key: str, enabled: bool) -> bool:
|
||||
"""Save Requestarr configuration settings"""
|
||||
try:
|
||||
with self.get_connection() as conn:
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO requestor_settings (id, tmdb_api_key, enabled, updated_at)
|
||||
INSERT OR REPLACE INTO requestarr_settings (id, tmdb_api_key, enabled, updated_at)
|
||||
VALUES (1, ?, ?, CURRENT_TIMESTAMP)
|
||||
""", (tmdb_api_key, enabled))
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving requestor settings: {e}")
|
||||
logger.error(f"Error saving requestarr settings: {e}")
|
||||
return False
|
||||
|
||||
def add_request(self, tmdb_id: int, media_type: str, title: str, year: int,
|
||||
@@ -2352,7 +2352,7 @@ class HuntarrDatabase:
|
||||
try:
|
||||
with self.get_connection() as conn:
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO requestor_requests
|
||||
INSERT OR REPLACE INTO requestarr_requests
|
||||
(tmdb_id, media_type, title, year, overview, poster_path, backdrop_path,
|
||||
app_type, instance_name, status, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP)
|
||||
@@ -2369,14 +2369,14 @@ class HuntarrDatabase:
|
||||
offset = (page - 1) * page_size
|
||||
with self.get_connection() as conn:
|
||||
# Get total count
|
||||
count_cursor = conn.execute("SELECT COUNT(*) FROM requestor_requests")
|
||||
count_cursor = conn.execute("SELECT COUNT(*) FROM requestarr_requests")
|
||||
total_requests = count_cursor.fetchone()[0]
|
||||
|
||||
# Get paginated requests
|
||||
cursor = conn.execute("""
|
||||
SELECT tmdb_id, media_type, title, year, overview, poster_path, backdrop_path,
|
||||
app_type, instance_name, status, request_date, updated_at
|
||||
FROM requestor_requests
|
||||
FROM requestarr_requests
|
||||
ORDER BY request_date DESC
|
||||
LIMIT ? OFFSET ?
|
||||
""", (page_size, offset))
|
||||
@@ -2415,7 +2415,7 @@ class HuntarrDatabase:
|
||||
try:
|
||||
with self.get_connection() as conn:
|
||||
conn.execute("""
|
||||
UPDATE requestor_requests
|
||||
UPDATE requestarr_requests
|
||||
SET status = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE tmdb_id = ? AND media_type = ? AND app_type = ? AND instance_name = ?
|
||||
""", (status, tmdb_id, media_type, app_type, instance_name))
|
||||
@@ -2429,7 +2429,7 @@ class HuntarrDatabase:
|
||||
try:
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.execute("""
|
||||
SELECT COUNT(*) FROM requestor_requests
|
||||
SELECT COUNT(*) FROM requestarr_requests
|
||||
WHERE tmdb_id = ? AND media_type = ? AND app_type = ? AND instance_name = ?
|
||||
""", (tmdb_id, media_type, app_type, instance_name))
|
||||
return cursor.fetchone()[0] > 0
|
||||
|
||||
@@ -39,7 +39,7 @@ from src.primary.auth import (
|
||||
from src.primary.routes.common import common_bp
|
||||
from src.primary.routes.plex_auth_routes import plex_auth_bp
|
||||
# Import blueprints for each app from the centralized blueprints module
|
||||
from src.primary.apps.blueprints import sonarr_bp, radarr_bp, lidarr_bp, readarr_bp, whisparr_bp, eros_bp, swaparr_bp, requestor_bp
|
||||
from src.primary.apps.blueprints import sonarr_bp, radarr_bp, lidarr_bp, readarr_bp, whisparr_bp, eros_bp, swaparr_bp, requestarr_bp
|
||||
|
||||
# Import stateful blueprint
|
||||
from src.primary.stateful_routes import stateful_api
|
||||
@@ -271,7 +271,7 @@ app.register_blueprint(readarr_bp, url_prefix='/api/readarr')
|
||||
app.register_blueprint(whisparr_bp, url_prefix='/api/whisparr')
|
||||
app.register_blueprint(eros_bp, url_prefix='/api/eros')
|
||||
app.register_blueprint(swaparr_bp, url_prefix='/api/swaparr')
|
||||
app.register_blueprint(requestor_bp)
|
||||
app.register_blueprint(requestarr_bp)
|
||||
app.register_blueprint(stateful_api, url_prefix='/api/stateful')
|
||||
app.register_blueprint(history_blueprint, url_prefix='/api/hunt-manager')
|
||||
app.register_blueprint(scheduler_api)
|
||||
|
||||
Reference in New Issue
Block a user