fix: Race Condition bei gleichzeitigen Downloads in savetv_web.py
- Thread-Lock fuer Progress-Datei: verhindert korruptes JSON bei vielen simultanen Downloads - start_new_session=True in Popen: wget-Prozesse ueberleben Service-Neustarts - Atomic load+modify+save Pattern via _load_progress_raw/_save_progress_raw
This commit is contained in:
parent
25642d6b62
commit
32da34b3c2
1 changed files with 31 additions and 13 deletions
|
|
@ -43,9 +43,11 @@ def _save_download_log(log):
|
||||||
|
|
||||||
DOWNLOAD_PROGRESS = Path("/mnt/savetv/.download_progress.json")
|
DOWNLOAD_PROGRESS = Path("/mnt/savetv/.download_progress.json")
|
||||||
DOWNLOAD_DIR = Path("/mnt/savetv")
|
DOWNLOAD_DIR = Path("/mnt/savetv")
|
||||||
|
_PROGRESS_LOCK = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def _load_progress():
|
def _load_progress_raw():
|
||||||
|
"""Liest Progress-Datei ohne Lock (nur innerhalb von _PROGRESS_LOCK aufrufen)."""
|
||||||
if DOWNLOAD_PROGRESS.exists():
|
if DOWNLOAD_PROGRESS.exists():
|
||||||
try:
|
try:
|
||||||
return json.loads(DOWNLOAD_PROGRESS.read_text())
|
return json.loads(DOWNLOAD_PROGRESS.read_text())
|
||||||
|
|
@ -54,11 +56,22 @@ def _load_progress():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def _save_progress_raw(prog):
|
||||||
|
"""Schreibt Progress-Datei ohne Lock (nur innerhalb von _PROGRESS_LOCK aufrufen)."""
|
||||||
|
DOWNLOAD_PROGRESS.write_text(json.dumps(prog, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def _load_progress():
|
||||||
|
with _PROGRESS_LOCK:
|
||||||
|
return _load_progress_raw()
|
||||||
|
|
||||||
|
|
||||||
def _save_progress(prog):
|
def _save_progress(prog):
|
||||||
try:
|
with _PROGRESS_LOCK:
|
||||||
DOWNLOAD_PROGRESS.write_text(json.dumps(prog, ensure_ascii=False, indent=2))
|
try:
|
||||||
except Exception:
|
_save_progress_raw(prog)
|
||||||
pass
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _head_content_length(url):
|
def _head_content_length(url):
|
||||||
|
|
@ -722,12 +735,14 @@ def api_download():
|
||||||
filename = f"{safe_title}_{tid}.mp4"
|
filename = f"{safe_title}_{tid}.mp4"
|
||||||
target = DOWNLOAD_DIR / filename
|
target = DOWNLOAD_DIR / filename
|
||||||
|
|
||||||
progress[str(tid)] = {
|
with _PROGRESS_LOCK:
|
||||||
"filename": filename,
|
cur_progress = _load_progress_raw()
|
||||||
"expected_bytes": expected_bytes,
|
cur_progress[str(tid)] = {
|
||||||
"started_at": datetime.now().isoformat(),
|
"filename": filename,
|
||||||
}
|
"expected_bytes": expected_bytes,
|
||||||
_save_progress(progress)
|
"started_at": datetime.now().isoformat(),
|
||||||
|
}
|
||||||
|
_save_progress_raw(cur_progress)
|
||||||
|
|
||||||
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
|
|
@ -735,10 +750,13 @@ def api_download():
|
||||||
["wget", "-q", "-O", str(target), url],
|
["wget", "-q", "-O", str(target), url],
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
|
start_new_session=True,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
progress.pop(str(tid), None)
|
with _PROGRESS_LOCK:
|
||||||
_save_progress(progress)
|
cur_progress = _load_progress_raw()
|
||||||
|
cur_progress.pop(str(tid), None)
|
||||||
|
_save_progress_raw(cur_progress)
|
||||||
return {"tid": tid, "ok": False, "error": f"wget: {e}"}
|
return {"tid": tid, "ok": False, "error": f"wget: {e}"}
|
||||||
|
|
||||||
dl_log[str(tid)] = "running"
|
dl_log[str(tid)] = "running"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue