Add graceful shutdown, logging, streaming with Range Requests, and state.json cache
- Graceful shutdown: SIGTERM/SIGINT handlers stop continuous scraper, disconnect Telegram client, drain running jobs with timeout - Logging: replace print() with logging module throughout web server, basicConfig in main.py with structured log format - Streaming: chunked file reads (64KB) instead of loading entire files into memory; full HTTP Range Request support (206 Partial Content) for video seeking in the browser - Cache: TTL-based in-memory cache (1s) for state.json reads in StateStore, thread-safe with existing RLock - Bugfix: send_error_json now converts HTTPStatus to int for JSON
This commit is contained in:
+21
-4
@@ -1,7 +1,8 @@
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict
|
||||
from typing import Any, Callable, Dict, Optional
|
||||
|
||||
|
||||
DEFAULT_STATE: Dict[str, Any] = {
|
||||
@@ -24,17 +25,32 @@ class StateStore:
|
||||
def __init__(self, path: Path):
|
||||
self.path = path
|
||||
self.lock = threading.RLock()
|
||||
self._cache: Optional[Dict[str, Any]] = None
|
||||
self._cache_time: float = 0
|
||||
self._cache_ttl: float = 1.0
|
||||
|
||||
def load(self) -> Dict[str, Any]:
|
||||
with self.lock:
|
||||
now = time.time()
|
||||
if self._cache is not None and (now - self._cache_time) < self._cache_ttl:
|
||||
return dict(self._cache)
|
||||
if not self.path.exists():
|
||||
return self._default_state()
|
||||
result = self._default_state()
|
||||
self._cache = result
|
||||
self._cache_time = now
|
||||
return result
|
||||
try:
|
||||
with self.path.open("r", encoding="utf-8") as handle:
|
||||
state = json.load(handle)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return self._default_state()
|
||||
return self._merge_defaults(state)
|
||||
result = self._default_state()
|
||||
self._cache = result
|
||||
self._cache_time = now
|
||||
return result
|
||||
result = self._merge_defaults(state)
|
||||
self._cache = result
|
||||
self._cache_time = now
|
||||
return result
|
||||
|
||||
def save(self, state: Dict[str, Any]) -> None:
|
||||
with self.lock:
|
||||
@@ -44,6 +60,7 @@ class StateStore:
|
||||
json.dump(self._merge_defaults(state), handle, ensure_ascii=False, indent=2)
|
||||
handle.write("\n")
|
||||
tmp_path.replace(self.path)
|
||||
self._cache = None
|
||||
|
||||
def update(self, mutator: Callable[[Dict[str, Any]], None]) -> Dict[str, Any]:
|
||||
with self.lock:
|
||||
|
||||
Reference in New Issue
Block a user