Initial release — MediaTracker v1.0
Dockerized media library scanner for NAS drives. Identifies movies and series from filenames, fetches posters via the public IMDB suggestion API (no key required), and exposes a dark-themed web UI with multilingual search support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
98
backend/routers/scan.py
Normal file
98
backend/routers/scan.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
|
||||
from database import ScanHistory, get_session
|
||||
from scheduler import (
|
||||
get_active_scan_id,
|
||||
is_scan_running,
|
||||
trigger_full_scan,
|
||||
trigger_incremental_scan,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api/scan", tags=["scan"])
|
||||
|
||||
|
||||
@router.post("/full")
|
||||
def start_full_scan(request: Request):
|
||||
settings = request.app.state.settings
|
||||
if is_scan_running():
|
||||
raise HTTPException(status_code=409, detail="A scan is already running")
|
||||
scan_id = trigger_full_scan(settings)
|
||||
if scan_id is None:
|
||||
raise HTTPException(status_code=409, detail="A scan is already running")
|
||||
return {"scan_id": scan_id, "type": "full", "status": "running"}
|
||||
|
||||
|
||||
@router.post("/incremental")
|
||||
def start_incremental_scan(request: Request):
|
||||
settings = request.app.state.settings
|
||||
if is_scan_running():
|
||||
raise HTTPException(status_code=409, detail="A scan is already running")
|
||||
scan_id = trigger_incremental_scan(settings)
|
||||
if scan_id is None:
|
||||
raise HTTPException(status_code=409, detail="A scan is already running")
|
||||
return {"scan_id": scan_id, "type": "incremental", "status": "running"}
|
||||
|
||||
|
||||
@router.get("/status/{scan_id}")
|
||||
def get_scan_status(scan_id: int):
|
||||
session = get_session()
|
||||
try:
|
||||
record = session.query(ScanHistory).get(scan_id)
|
||||
if record is None:
|
||||
raise HTTPException(status_code=404, detail="Scan not found")
|
||||
return {
|
||||
"scan_id": record.id,
|
||||
"type": record.scan_type,
|
||||
"status": record.status,
|
||||
"started_at": record.started_at.isoformat() if record.started_at else None,
|
||||
"finished_at": record.finished_at.isoformat() if record.finished_at else None,
|
||||
"files_found": record.files_found,
|
||||
"files_added": record.files_added,
|
||||
"files_removed": record.files_removed,
|
||||
"files_unmatched": record.files_unmatched,
|
||||
"error_message": record.error_message,
|
||||
}
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
@router.post("/fetch-alt-titles")
|
||||
def fetch_alt_titles_now():
|
||||
"""Trigger a Wikidata alt-title fetch in the background (no file scan)."""
|
||||
def _run():
|
||||
session = get_session()
|
||||
try:
|
||||
_fetch_alt_titles(session)
|
||||
finally:
|
||||
session.close()
|
||||
threading.Thread(target=_run, daemon=True, name="alt-titles-fetch").start()
|
||||
return {"status": "started"}
|
||||
|
||||
|
||||
@router.get("/history")
|
||||
def get_scan_history():
|
||||
session = get_session()
|
||||
try:
|
||||
records = (
|
||||
session.query(ScanHistory)
|
||||
.order_by(ScanHistory.started_at.desc())
|
||||
.limit(20)
|
||||
.all()
|
||||
)
|
||||
return [
|
||||
{
|
||||
"scan_id": r.id,
|
||||
"type": r.scan_type,
|
||||
"status": r.status,
|
||||
"started_at": r.started_at.isoformat() if r.started_at else None,
|
||||
"finished_at": r.finished_at.isoformat() if r.finished_at else None,
|
||||
"files_found": r.files_found,
|
||||
"files_added": r.files_added,
|
||||
"files_removed": r.files_removed,
|
||||
"files_unmatched": r.files_unmatched,
|
||||
"error_message": r.error_message,
|
||||
}
|
||||
for r in records
|
||||
]
|
||||
finally:
|
||||
session.close()
|
||||
Reference in New Issue
Block a user