formatting

This commit is contained in:
2026-02-07 21:26:10 -05:00
parent 435fcc3b2e
commit 9d16c96490
4 changed files with 117 additions and 98 deletions

View File

@@ -41,6 +41,7 @@ logger = logging.getLogger(__name__)
class ConnectionStatus(Enum): class ConnectionStatus(Enum):
"""Connection status enum for better tracking.""" """Connection status enum for better tracking."""
DISCONNECTED = "disconnected" DISCONNECTED = "disconnected"
CONNECTING = "connecting" CONNECTING = "connecting"
CONNECTED = "connected" CONNECTED = "connected"
@@ -222,7 +223,9 @@ class Lighting:
"last_error": self._state.last_error, "last_error": self._state.last_error,
"updated_at": time.time(), "updated_at": time.time(),
} }
self.redis_client.set(self.REDIS_STATUS_KEY, json.dumps(status_data), ex=300) self.redis_client.set(
self.REDIS_STATUS_KEY, json.dumps(status_data), ex=300
)
except Exception as e: except Exception as e:
logger.debug(f"Failed to update status in Redis: {e}") logger.debug(f"Failed to update status in Redis: {e}")
@@ -458,7 +461,9 @@ class Lighting:
# Set status and start polling Redis # Set status and start polling Redis
self._state.status = ConnectionStatus.AWAITING_2FA self._state.status = ConnectionStatus.AWAITING_2FA
self._state.last_error = "2FA code required - check email and submit via API or Redis" self._state.last_error = (
"2FA code required - check email and submit via API or Redis"
)
self._update_status_in_redis() self._update_status_in_redis()
logger.warning( logger.warning(
@@ -902,11 +907,14 @@ class Lighting:
# Try to get from Redis first (more up-to-date) # Try to get from Redis first (more up-to-date)
cached = self.redis_client.get(self.REDIS_STATUS_KEY) cached = self.redis_client.get(self.REDIS_STATUS_KEY)
if cached: if cached:
data = json.loads(cached.decode() if isinstance(cached, bytes) else str(cached)) data = json.loads(
cached.decode() if isinstance(cached, bytes) else str(cached)
)
return JSONResponse(content=data) return JSONResponse(content=data)
# Fall back to current state # Fall back to current state
return JSONResponse(content={ return JSONResponse(
content={
"status": self._state.status.value, "status": self._state.status.value,
"connected_at": self._state.connected_at, "connected_at": self._state.connected_at,
"last_command_at": self._state.last_command_at, "last_command_at": self._state.last_command_at,
@@ -914,12 +922,12 @@ class Lighting:
"consecutive_failures": self._state.consecutive_failures, "consecutive_failures": self._state.consecutive_failures,
"last_error": self._state.last_error, "last_error": self._state.last_error,
"updated_at": time.time(), "updated_at": time.time(),
}) }
)
except Exception as e: except Exception as e:
logger.error(f"Error getting connection status: {e}") logger.error(f"Error getting connection status: {e}")
return JSONResponse( return JSONResponse(
status_code=500, status_code=500, content={"error": str(e), "status": "unknown"}
content={"error": str(e), "status": "unknown"}
) )
async def submit_2fa_code(self, request: Request) -> JSONResponse: async def submit_2fa_code(self, request: Request) -> JSONResponse:
@@ -936,7 +944,9 @@ class Lighting:
code = body.get("code", "").strip() code = body.get("code", "").strip()
if not code: if not code:
raise HTTPException(status_code=400, detail="Missing 'code' in request body") raise HTTPException(
status_code=400, detail="Missing 'code' in request body"
)
if not code.isdigit() or len(code) != 6: if not code.isdigit() or len(code) != 6:
raise HTTPException(status_code=400, detail="Code must be 6 digits") raise HTTPException(status_code=400, detail="Code must be 6 digits")
@@ -946,11 +956,13 @@ class Lighting:
logger.info("2FA code submitted via API") logger.info("2FA code submitted via API")
return JSONResponse(content={ return JSONResponse(
content={
"message": "2FA code submitted successfully", "message": "2FA code submitted successfully",
"status": self._state.status.value, "status": self._state.status.value,
"note": "The code will be used on the next authentication attempt" "note": "The code will be used on the next authentication attempt",
}) }
)
except HTTPException: except HTTPException:
raise raise
@@ -964,7 +976,9 @@ class Lighting:
Requires admin or lighting role. Requires admin or lighting role.
""" """
if "lighting" not in user.get("roles", []) and "admin" not in user.get("roles", []): if "lighting" not in user.get("roles", []) and "admin" not in user.get(
"roles", []
):
raise HTTPException(status_code=403, detail="Insufficient permissions") raise HTTPException(status_code=403, detail="Insufficient permissions")
try: try:
@@ -974,18 +988,20 @@ class Lighting:
await self._connect(force=True) await self._connect(force=True)
return JSONResponse(content={ return JSONResponse(
content={
"message": "Reconnection successful", "message": "Reconnection successful",
"status": self._state.status.value, "status": self._state.status.value,
}) }
)
except TwoFactorRequiredError: except TwoFactorRequiredError:
return JSONResponse( return JSONResponse(
status_code=202, status_code=202,
content={ content={
"message": "Reconnection requires 2FA", "message": "Reconnection requires 2FA",
"status": ConnectionStatus.AWAITING_2FA.value, "status": ConnectionStatus.AWAITING_2FA.value,
"action": "Submit 2FA code via POST /lighting/2fa" "action": "Submit 2FA code via POST /lighting/2fa",
} },
) )
except Exception as e: except Exception as e:
logger.error(f"Force reconnect failed: {e}") logger.error(f"Force reconnect failed: {e}")

View File

@@ -21,25 +21,25 @@ def normalize_for_search(s: str) -> str:
s = s.lower().strip() s = s.lower().strip()
# Remove parenthetical content: (Remastered), (feat. X), (2020 Remix), etc. # Remove parenthetical content: (Remastered), (feat. X), (2020 Remix), etc.
s = re.sub(r'\s*\([^)]*\)\s*', ' ', s) s = re.sub(r"\s*\([^)]*\)\s*", " ", s)
# Remove bracketed content: [Explicit], [Deluxe Edition], etc. # Remove bracketed content: [Explicit], [Deluxe Edition], etc.
s = re.sub(r'\s*\[[^\]]*\]\s*', ' ', s) s = re.sub(r"\s*\[[^\]]*\]\s*", " ", s)
# Remove "feat.", "ft.", "featuring" and everything after # Remove "feat.", "ft.", "featuring" and everything after
s = re.sub(r'\s*(feat\.?|ft\.?|featuring)\s+.*$', '', s, flags=re.IGNORECASE) s = re.sub(r"\s*(feat\.?|ft\.?|featuring)\s+.*$", "", s, flags=re.IGNORECASE)
# Remove "The " prefix from artist names # Remove "The " prefix from artist names
s = re.sub(r'^the\s+', '', s) s = re.sub(r"^the\s+", "", s)
# Normalize & to "and" # Normalize & to "and"
s = re.sub(r'\s*&\s*', ' and ', s) s = re.sub(r"\s*&\s*", " and ", s)
# Remove punctuation except spaces # Remove punctuation except spaces
s = re.sub(r"[^\w\s]", '', s) s = re.sub(r"[^\w\s]", "", s)
# Collapse multiple spaces # Collapse multiple spaces
s = re.sub(r'\s+', ' ', s).strip() s = re.sub(r"\s+", " ", s).strip()
return s return s

View File

@@ -14,6 +14,7 @@ Usage examples:
- Disable notifications: ./migrate_sqlite_to_pg.py --no-notify - Disable notifications: ./migrate_sqlite_to_pg.py --no-notify
- Force re-import: ./migrate_sqlite_to_pg.py --force - Force re-import: ./migrate_sqlite_to_pg.py --force
""" """
from __future__ import annotations from __future__ import annotations
import argparse import argparse
@@ -391,7 +392,9 @@ def terminate_connections(db_name: str, max_wait: int = 10) -> bool:
conn.close() conn.close()
if remaining > 0: if remaining > 0:
print(f" Warning: {remaining} connection(s) still active (may be superuser sessions)") print(
f" Warning: {remaining} connection(s) still active (may be superuser sessions)"
)
return False return False
return True return True
@@ -666,9 +669,7 @@ Examples:
print(f"New dump available: {dump_date_str}") print(f"New dump available: {dump_date_str}")
if notify_enabled: if notify_enabled:
asyncio.run( asyncio.run(notify_new_dump_found(latest["filename"], dump_date_str))
notify_new_dump_found(latest["filename"], dump_date_str)
)
# Download # Download
print(f"\nDownloading {latest['filename']}...") print(f"\nDownloading {latest['filename']}...")

View File

@@ -521,7 +521,9 @@ async def download_and_extract_dump(
# If an extracted sqlite file already exists, skip download and extraction # If an extracted sqlite file already exists, skip download and extraction
if sqlite_path.exists() and sqlite_path.stat().st_size > 0: if sqlite_path.exists() and sqlite_path.stat().st_size > 0:
print(f"Found existing extracted SQLite file {sqlite_path}; skipping download/extract") print(
f"Found existing extracted SQLite file {sqlite_path}; skipping download/extract"
)
return str(sqlite_path), None return str(sqlite_path), None
# Streaming download with retry and resume support # Streaming download with retry and resume support