This commit is contained in:
@@ -1897,4 +1897,326 @@ def register_database_admin_routes(app, dependencies):
|
||||
"query": "SELECT imdb_id, season, episode, dateadded, source FROM episodes WHERE has_video_file = FALSE ORDER BY imdb_id, season, episode"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Scheduled Scans Endpoints
|
||||
|
||||
@app.get("/api/admin/scheduled-scans")
|
||||
async def api_get_scheduled_scans():
|
||||
"""Get all scheduled scans"""
|
||||
return await get_scheduled_scans(dependencies)
|
||||
|
||||
@app.post("/api/admin/scheduled-scans")
|
||||
async def api_create_scheduled_scan(request: CreateScheduledScanRequest):
|
||||
"""Create a new scheduled scan"""
|
||||
return await create_scheduled_scan(dependencies, request)
|
||||
|
||||
@app.put("/api/admin/scheduled-scans/{scan_id}")
|
||||
async def api_update_scheduled_scan(scan_id: int, request: UpdateScheduledScanRequest):
|
||||
"""Update a scheduled scan"""
|
||||
return await update_scheduled_scan(dependencies, scan_id, request)
|
||||
|
||||
@app.delete("/api/admin/scheduled-scans/{scan_id}")
|
||||
async def api_delete_scheduled_scan(scan_id: int):
|
||||
"""Delete a scheduled scan"""
|
||||
return await delete_scheduled_scan(dependencies, scan_id)
|
||||
|
||||
@app.post("/api/admin/scheduled-scans/{scan_id}/toggle")
|
||||
async def api_toggle_scheduled_scan(scan_id: int):
|
||||
"""Toggle a scheduled scan enabled/disabled"""
|
||||
return await toggle_scheduled_scan(dependencies, scan_id)
|
||||
|
||||
@app.post("/api/admin/scheduled-scans/{scan_id}/run")
|
||||
async def api_run_scheduled_scan(scan_id: int):
|
||||
"""Manually run a scheduled scan"""
|
||||
return await run_scheduled_scan(dependencies, scan_id)
|
||||
|
||||
@app.get("/api/admin/scheduled-scans/executions")
|
||||
async def api_get_schedule_executions(schedule_id: int = None):
|
||||
"""Get schedule execution history"""
|
||||
return await get_schedule_executions(dependencies, schedule_id)
|
||||
|
||||
|
||||
# Scheduled Scans Functions
|
||||
|
||||
async def get_scheduled_scans(dependencies: dict):
|
||||
"""Get all scheduled scans"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
scans = db.get_scheduled_scans()
|
||||
|
||||
# Convert to response format
|
||||
scan_list = []
|
||||
for scan in scans:
|
||||
scan_dict = dict(scan)
|
||||
# Convert datetime objects to strings
|
||||
for field in ['created_at', 'updated_at', 'last_run_at', 'next_run_at']:
|
||||
if scan_dict.get(field):
|
||||
scan_dict[field] = scan_dict[field].isoformat()
|
||||
scan_list.append(scan_dict)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"scans": scan_list
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def create_scheduled_scan(dependencies: dict, request: CreateScheduledScanRequest):
|
||||
"""Create a new scheduled scan"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
|
||||
# Validate cron expression
|
||||
from croniter import croniter
|
||||
if not croniter.is_valid(request.cron_expression):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid cron expression"
|
||||
}
|
||||
|
||||
# Validate media type and scan mode
|
||||
if request.media_type not in ['tv', 'movies', 'both']:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid media type. Must be 'tv', 'movies', or 'both'"
|
||||
}
|
||||
|
||||
if request.scan_mode not in ['smart', 'full', 'incomplete']:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid scan mode. Must be 'smart', 'full', or 'incomplete'"
|
||||
}
|
||||
|
||||
# Create the scheduled scan
|
||||
scan_id = db.create_scheduled_scan(
|
||||
name=request.name,
|
||||
description=request.description,
|
||||
cron_expression=request.cron_expression,
|
||||
media_type=request.media_type,
|
||||
scan_mode=request.scan_mode,
|
||||
specific_paths=request.specific_paths,
|
||||
enabled=request.enabled,
|
||||
created_by="web_interface"
|
||||
)
|
||||
|
||||
# Calculate next run time
|
||||
from croniter import croniter
|
||||
from datetime import datetime, timezone
|
||||
cron = croniter(request.cron_expression, datetime.now(timezone.utc))
|
||||
next_run = cron.get_next(datetime)
|
||||
db.update_scan_next_run(scan_id, next_run)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"scan_id": scan_id,
|
||||
"message": f"Scheduled scan '{request.name}' created successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def update_scheduled_scan(dependencies: dict, scan_id: int, request: UpdateScheduledScanRequest):
|
||||
"""Update a scheduled scan"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
|
||||
# Check if scan exists
|
||||
existing_scan = db.get_scheduled_scan(scan_id)
|
||||
if not existing_scan:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Scheduled scan not found"
|
||||
}
|
||||
|
||||
# Validate cron expression if provided
|
||||
if request.cron_expression:
|
||||
from croniter import croniter
|
||||
if not croniter.is_valid(request.cron_expression):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid cron expression"
|
||||
}
|
||||
|
||||
# Validate media type and scan mode if provided
|
||||
if request.media_type and request.media_type not in ['tv', 'movies', 'both']:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid media type. Must be 'tv', 'movies', or 'both'"
|
||||
}
|
||||
|
||||
if request.scan_mode and request.scan_mode not in ['smart', 'full', 'incomplete']:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid scan mode. Must be 'smart', 'full', or 'incomplete'"
|
||||
}
|
||||
|
||||
# Update the scheduled scan
|
||||
success = db.update_scheduled_scan(
|
||||
scan_id=scan_id,
|
||||
name=request.name,
|
||||
description=request.description,
|
||||
cron_expression=request.cron_expression,
|
||||
media_type=request.media_type,
|
||||
scan_mode=request.scan_mode,
|
||||
specific_paths=request.specific_paths,
|
||||
enabled=request.enabled,
|
||||
updated_by="web_interface"
|
||||
)
|
||||
|
||||
if not success:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Failed to update scheduled scan"
|
||||
}
|
||||
|
||||
# Update next run time if cron expression changed
|
||||
if request.cron_expression:
|
||||
from croniter import croniter
|
||||
from datetime import datetime, timezone
|
||||
cron = croniter(request.cron_expression, datetime.now(timezone.utc))
|
||||
next_run = cron.get_next(datetime)
|
||||
db.update_scan_next_run(scan_id, next_run)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Scheduled scan updated successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def delete_scheduled_scan(dependencies: dict, scan_id: int):
|
||||
"""Delete a scheduled scan"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
|
||||
# Check if scan exists
|
||||
existing_scan = db.get_scheduled_scan(scan_id)
|
||||
if not existing_scan:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Scheduled scan not found"
|
||||
}
|
||||
|
||||
# Delete the scheduled scan
|
||||
success = db.delete_scheduled_scan(scan_id)
|
||||
|
||||
if not success:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Failed to delete scheduled scan"
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Scheduled scan '{existing_scan['name']}' deleted successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def toggle_scheduled_scan(dependencies: dict, scan_id: int):
|
||||
"""Toggle a scheduled scan enabled/disabled"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
|
||||
# Check if scan exists
|
||||
existing_scan = db.get_scheduled_scan(scan_id)
|
||||
if not existing_scan:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Scheduled scan not found"
|
||||
}
|
||||
|
||||
# Toggle enabled status
|
||||
new_enabled = not existing_scan['enabled']
|
||||
success = db.update_scheduled_scan(
|
||||
scan_id=scan_id,
|
||||
enabled=new_enabled,
|
||||
updated_by="web_interface"
|
||||
)
|
||||
|
||||
if not success:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Failed to toggle scheduled scan"
|
||||
}
|
||||
|
||||
status = "enabled" if new_enabled else "disabled"
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Scheduled scan '{existing_scan['name']}' {status} successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def run_scheduled_scan(dependencies: dict, scan_id: int):
|
||||
"""Manually run a scheduled scan"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
|
||||
# Check if scan exists
|
||||
existing_scan = db.get_scheduled_scan(scan_id)
|
||||
if not existing_scan:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Scheduled scan not found"
|
||||
}
|
||||
|
||||
# TODO: Implement actual scan execution
|
||||
# For now, just return a success message
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Manual execution of '{existing_scan['name']}' started",
|
||||
"note": "Scan execution will be implemented with the background scheduler"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
async def get_schedule_executions(dependencies: dict, schedule_id: int = None):
|
||||
"""Get schedule execution history"""
|
||||
try:
|
||||
db = dependencies["db"]
|
||||
executions = db.get_schedule_executions(schedule_id)
|
||||
|
||||
# Convert to response format
|
||||
execution_list = []
|
||||
for execution in executions:
|
||||
execution_dict = dict(execution)
|
||||
# Convert datetime objects to strings
|
||||
for field in ['started_at', 'completed_at']:
|
||||
if execution_dict.get(field):
|
||||
execution_dict[field] = execution_dict[field].isoformat()
|
||||
execution_list.append(execution_dict)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"executions": execution_list
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
Reference in New Issue
Block a user