feat: add parallel file scanning with dedicated executor and support for scanning all sources simultaneously
Add scan_executor ThreadPoolExecutor with 4 workers for parallel file scanning. Import as_completed from concurrent.futures to process scan results. Replace sequential scan_one() loop with parallel futures submission and result collection using as_completed(). Update /api/rescan endpoint to accept "all" source parameter for scanning both Radarr and Sonarr simultaneously. Fix cache deletion
This commit is contained in:
@@ -7,7 +7,7 @@ import threading
|
||||
import time
|
||||
import shutil
|
||||
import re
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -31,6 +31,7 @@ logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO").upper(), format="%
|
||||
log = logging.getLogger("radarr-language-dashboard")
|
||||
app = Flask(__name__)
|
||||
executor = ThreadPoolExecutor(max_workers=int(os.environ.get("SCAN_WORKERS", "2")))
|
||||
scan_executor = ThreadPoolExecutor(max_workers=int(os.environ.get("SCAN_WORKERS", "4")))
|
||||
jobs = {"radarr": {"state": "idle", "total": 0, "done": 0, "error": None}, "sonarr": {"state": "idle", "total": 0, "done": 0, "error": None}}
|
||||
job_lock = threading.Lock()
|
||||
system_lock = threading.Lock()
|
||||
@@ -283,8 +284,9 @@ def start_scan(source, force=False):
|
||||
for group in sonarr_groups():
|
||||
paths.extend(r["local_path"] for r in group["episodes"] if r["local_path"] != "—" and r["pending"])
|
||||
with job_lock: jobs[source]["total"] = len(paths)
|
||||
for path in paths:
|
||||
scan_one(path)
|
||||
futures = [scan_executor.submit(scan_one, path) for path in paths]
|
||||
for future in as_completed(futures):
|
||||
future.result()
|
||||
with job_lock: jobs[source]["done"] += 1
|
||||
with job_lock: jobs[source]["state"] = "done"
|
||||
except Exception as exc:
|
||||
@@ -329,11 +331,17 @@ def index():
|
||||
@app.post("/api/rescan")
|
||||
def api_rescan():
|
||||
source = request.args.get("source", "radarr")
|
||||
if source not in jobs: return jsonify({"ok":False, "error":"Unbekannte Quelle"}), 400
|
||||
if source not in (*jobs.keys(), "all"): return jsonify({"ok":False, "error":"Unbekannte Quelle"}), 400
|
||||
con = db()
|
||||
if source == "radarr": con.execute("DELETE FROM media_cache")
|
||||
else: con.execute("DELETE FROM media_cache WHERE path LIKE ?", (LOCAL_SERIES_PATH.rstrip("/") + "/%",))
|
||||
con.commit(); con.close(); start_scan(source, force=True)
|
||||
if source in ("radarr", "all"): con.execute("DELETE FROM media_cache WHERE path NOT LIKE ?", (LOCAL_SERIES_PATH.rstrip("/") + "/%",))
|
||||
if source in ("sonarr", "all"): con.execute("DELETE FROM media_cache WHERE path LIKE ?", (LOCAL_SERIES_PATH.rstrip("/") + "/%",))
|
||||
con.commit(); con.close()
|
||||
if source == "all":
|
||||
start_scan("radarr", force=True)
|
||||
if SONARR_URL and SONARR_API_KEY:
|
||||
start_scan("sonarr", force=True)
|
||||
else:
|
||||
start_scan(source, force=True)
|
||||
return jsonify({"ok":True})
|
||||
|
||||
@app.get("/api/scan-status")
|
||||
|
||||
Reference in New Issue
Block a user