alisaadhq's picture
Upload 19 files
d286c92 verified
Raw
History Blame Contribute Delete
3.3 kB
"""Per-browser user sessions and idle cleanup."""
import os
import secrets
import shutil
import threading
import time
from fastapi import Request
from torrent2drive.config import BASE_TEMP
from torrent2drive.constants import DEFAULT_ENC
class UserSession:
"""Isolated state for a single browser session / user."""
def __init__(self, sid: str):
self.sid = sid
self.lock = threading.Lock()
self.last_seen = time.time()
self.dl_dir = os.path.join(BASE_TEMP, sid, "downloads")
os.makedirs(self.dl_dir, exist_ok=True)
self.handle = None
self.ti = None
self.selected_file_indices = []
self.download_started = False
self.download_finished = False
self.is_transferring = False
self.is_completed = False
self.transfer_status = ""
self.use_ffmpeg = False
self.ff_progress = 0.0
self.ff_eta = ""
self.ff_speed = ""
self.ff_size_done = ""
self.ff_file_label = ""
self.ff_running = False
self.ff_stage = ""
self.ff_duration_s = 0.0
self.ff_time_done_s = 0.0
self.pipeline_total = 0
self.pipeline_current = 0
self.pipeline_current_name = ""
self.pipeline_failed: list[str] = []
self.pipeline_skipped: list[str] = []
self.pipeline_done: list[str] = []
self.pipeline_overall_pct = 0.0
self.pipeline_error = ""
self.enc = dict(DEFAULT_ENC)
self.stream_watch_only = False
self.stream_watch_active = False
self.stream_watch_index = -1
def touch(self):
self.last_seen = time.time()
_sessions: dict[str, UserSession] = {}
_sessions_lock = threading.Lock()
def get_or_create_session(request: Request) -> UserSession:
sid = request.session.get("sid")
if not sid:
sid = secrets.token_urlsafe(16)
request.session["sid"] = sid
with _sessions_lock:
us = _sessions.get(sid)
if us is None:
us = UserSession(sid)
_sessions[sid] = us
us.touch()
return us
def get_session_by_sid(sid: str) -> UserSession | None:
with _sessions_lock:
return _sessions.get(sid)
def _gc_sessions(idle_seconds=3600 * 6):
now = time.time()
drop = []
with _sessions_lock:
for sid, us in _sessions.items():
if (
us.is_transferring
or us.ff_running
or us.download_started
or us.stream_watch_active
):
continue
if now - us.last_seen > idle_seconds:
drop.append(sid)
for sid in drop:
us = _sessions.pop(sid, None)
if not us:
continue
try:
from torrent2drive.torrent_engine import remove_handle
remove_handle(us.handle)
except Exception:
pass
try:
shutil.rmtree(os.path.join(BASE_TEMP, sid), ignore_errors=True)
except Exception:
pass
def _gc_loop():
while True:
try:
_gc_sessions()
except Exception as e:
print(f"[GC] {e}")
time.sleep(900)
def start_gc_loop():
threading.Thread(target=_gc_loop, daemon=True).start()