Codebase audit fixes: stability, performance, accessibility

- Fix CORS: set allow_credentials=False (token auth, not cookies)
- Add threading.Lock for position cache thread safety
- Add shutdown_executor() for clean ThreadPoolExecutor cleanup
- Dedicated ThreadPoolExecutors for script/callback execution
- Fix Mutagen file handle leaks with try/finally close
- Reduce idle WebSocket polling (0.5s → 2.0s when no clients)
- Add :focus-visible styles for playback control buttons
- Add aria-label to icon-only header buttons
- Dynamic album art alt text for screen readers
- Persist MDI icon cache to localStorage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 12:10:24 +03:00
parent 73a6f387e1
commit 9404b37f05
9 changed files with 260 additions and 195 deletions

View File

@@ -2,6 +2,8 @@
import asyncio
import logging
import threading
import time as _time
from concurrent.futures import ThreadPoolExecutor
from typing import Optional, Any
@@ -16,8 +18,10 @@ _executor = ThreadPoolExecutor(max_workers=2, thread_name_prefix="winrt")
# Global storage for current album art (as bytes)
_current_album_art_bytes: bytes | None = None
# Lock protecting _position_cache and _track_skip_pending from concurrent access
_position_lock = threading.Lock()
# Global storage for position tracking
import time as _time
_position_cache = {
"track_id": "",
"base_position": 0.0,
@@ -224,124 +228,125 @@ def _sync_get_media_status() -> dict[str, Any]:
is_playing = result["state"] == "playing"
current_title = result.get('title', '')
# Check if track skip is pending and title changed
skip_just_completed = False
if _track_skip_pending["active"]:
if current_title and current_title != _track_skip_pending["old_title"]:
# Title changed - clear the skip flag and start grace period
_track_skip_pending["active"] = False
_track_skip_pending["old_title"] = ""
_track_skip_pending["grace_until"] = current_time + 300.0 # Long grace period
_track_skip_pending["stale_pos"] = -999 # Reset stale position tracking
skip_just_completed = True
# Reset position cache for new track
new_track_id = f"{current_title}:{result.get('artist', '')}:{result.get('duration', 0)}"
_position_cache["track_id"] = new_track_id
with _position_lock:
# Check if track skip is pending and title changed
skip_just_completed = False
if _track_skip_pending["active"]:
if current_title and current_title != _track_skip_pending["old_title"]:
# Title changed - clear the skip flag and start grace period
_track_skip_pending["active"] = False
_track_skip_pending["old_title"] = ""
_track_skip_pending["grace_until"] = current_time + 300.0 # Long grace period
_track_skip_pending["stale_pos"] = -999 # Reset stale position tracking
skip_just_completed = True
# Reset position cache for new track
new_track_id = f"{current_title}:{result.get('artist', '')}:{result.get('duration', 0)}"
_position_cache["track_id"] = new_track_id
_position_cache["base_position"] = 0.0
_position_cache["base_time"] = current_time
_position_cache["last_smtc_pos"] = -999 # Force fresh start
_position_cache["is_playing"] = is_playing
logger.debug(f"Track skip complete, new title: {current_title}, grace until: {_track_skip_pending['grace_until']}")
elif current_time - _track_skip_pending["skip_time"] > 5.0:
# Timeout after 5 seconds
_track_skip_pending["active"] = False
logger.debug("Track skip timeout")
# Check if we're in grace period (after skip, ignore high SMTC positions)
in_grace_period = current_time < _track_skip_pending.get("grace_until", 0)
# If track skip is pending or just completed, use cached/reset position
if _track_skip_pending["active"]:
pos = 0.0
_position_cache["base_position"] = 0.0
_position_cache["base_time"] = current_time
_position_cache["last_smtc_pos"] = -999 # Force fresh start
_position_cache["is_playing"] = is_playing
logger.debug(f"Track skip complete, new title: {current_title}, grace until: {_track_skip_pending['grace_until']}")
elif current_time - _track_skip_pending["skip_time"] > 5.0:
# Timeout after 5 seconds
_track_skip_pending["active"] = False
logger.debug("Track skip timeout")
elif skip_just_completed:
# Just completed skip - interpolate from 0
if is_playing:
elapsed = current_time - _position_cache["base_time"]
pos = elapsed
else:
pos = 0.0
elif in_grace_period:
# Grace period after track skip
# SMTC position is stale (from old track) and won't update until seek/pause
# We interpolate from 0 and only trust SMTC when it changes or reports low value
# Check if we're in grace period (after skip, ignore high SMTC positions)
in_grace_period = current_time < _track_skip_pending.get("grace_until", 0)
# Calculate interpolated position from start of new track
if is_playing:
elapsed = current_time - _position_cache.get("base_time", current_time)
interpolated_pos = _position_cache.get("base_position", 0.0) + elapsed
else:
interpolated_pos = _position_cache.get("base_position", 0.0)
# If track skip is pending or just completed, use cached/reset position
if _track_skip_pending["active"]:
pos = 0.0
_position_cache["base_position"] = 0.0
_position_cache["base_time"] = current_time
_position_cache["is_playing"] = is_playing
elif skip_just_completed:
# Just completed skip - interpolate from 0
if is_playing:
elapsed = current_time - _position_cache["base_time"]
pos = elapsed
# Get the stale position we've been tracking
stale_pos = _track_skip_pending.get("stale_pos", -999)
# Detect if SMTC position changed significantly from the stale value (user seeked)
smtc_changed = stale_pos >= 0 and abs(smtc_pos - stale_pos) > 3.0
# Trust SMTC if:
# 1. It reports a low position (indicating new track started)
# 2. It changed from the stale value (user seeked)
if smtc_pos < 10.0 or smtc_changed:
# SMTC is now trustworthy
_position_cache["base_position"] = smtc_pos
_position_cache["base_time"] = current_time
_position_cache["last_smtc_pos"] = smtc_pos
_position_cache["is_playing"] = is_playing
pos = smtc_pos
_track_skip_pending["grace_until"] = 0
_track_skip_pending["stale_pos"] = -999
logger.debug(f"Grace period: accepting SMTC pos {smtc_pos} (low={smtc_pos < 10}, changed={smtc_changed})")
else:
# SMTC is stale - keep interpolating
pos = interpolated_pos
# Record the stale position for change detection
if stale_pos < 0:
_track_skip_pending["stale_pos"] = smtc_pos
# Keep grace period active indefinitely while SMTC is stale
_track_skip_pending["grace_until"] = current_time + 300.0
logger.debug(f"Grace period: SMTC stale ({smtc_pos}), using interpolated {interpolated_pos}")
else:
pos = 0.0
elif in_grace_period:
# Grace period after track skip
# SMTC position is stale (from old track) and won't update until seek/pause
# We interpolate from 0 and only trust SMTC when it changes or reports low value
# Normal position tracking
# Create track ID from title + artist + duration
track_id = f"{current_title}:{result.get('artist', '')}:{result.get('duration', 0)}"
# Calculate interpolated position from start of new track
if is_playing:
elapsed = current_time - _position_cache.get("base_time", current_time)
interpolated_pos = _position_cache.get("base_position", 0.0) + elapsed
else:
interpolated_pos = _position_cache.get("base_position", 0.0)
# Detect if SMTC position changed (new track, seek, or state change)
smtc_pos_changed = abs(smtc_pos - _position_cache.get("last_smtc_pos", -999)) > 0.5
track_changed = track_id != _position_cache.get("track_id", "")
# Get the stale position we've been tracking
stale_pos = _track_skip_pending.get("stale_pos", -999)
if smtc_pos_changed or track_changed:
# SMTC updated - store new baseline
_position_cache["track_id"] = track_id
_position_cache["last_smtc_pos"] = smtc_pos
_position_cache["base_position"] = smtc_pos
_position_cache["base_time"] = current_time
_position_cache["is_playing"] = is_playing
pos = smtc_pos
elif is_playing:
# Interpolate position based on elapsed time
elapsed = current_time - _position_cache.get("base_time", current_time)
pos = _position_cache.get("base_position", smtc_pos) + elapsed
else:
# Paused - use base position
pos = _position_cache.get("base_position", smtc_pos)
# Detect if SMTC position changed significantly from the stale value (user seeked)
smtc_changed = stale_pos >= 0 and abs(smtc_pos - stale_pos) > 3.0
# Update playing state
if _position_cache.get("is_playing") != is_playing:
_position_cache["base_position"] = pos if is_playing else _position_cache.get("base_position", smtc_pos)
_position_cache["base_time"] = current_time
_position_cache["is_playing"] = is_playing
# Trust SMTC if:
# 1. It reports a low position (indicating new track started)
# 2. It changed from the stale value (user seeked)
if smtc_pos < 10.0 or smtc_changed:
# SMTC is now trustworthy
_position_cache["base_position"] = smtc_pos
_position_cache["base_time"] = current_time
_position_cache["last_smtc_pos"] = smtc_pos
_position_cache["is_playing"] = is_playing
pos = smtc_pos
_track_skip_pending["grace_until"] = 0
_track_skip_pending["stale_pos"] = -999
logger.debug(f"Grace period: accepting SMTC pos {smtc_pos} (low={smtc_pos < 10}, changed={smtc_changed})")
else:
# SMTC is stale - keep interpolating
pos = interpolated_pos
# Record the stale position for change detection
if stale_pos < 0:
_track_skip_pending["stale_pos"] = smtc_pos
# Keep grace period active indefinitely while SMTC is stale
_track_skip_pending["grace_until"] = current_time + 300.0
logger.debug(f"Grace period: SMTC stale ({smtc_pos}), using interpolated {interpolated_pos}")
else:
# Normal position tracking
# Create track ID from title + artist + duration
track_id = f"{current_title}:{result.get('artist', '')}:{result.get('duration', 0)}"
# Detect if SMTC position changed (new track, seek, or state change)
smtc_pos_changed = abs(smtc_pos - _position_cache.get("last_smtc_pos", -999)) > 0.5
track_changed = track_id != _position_cache.get("track_id", "")
if smtc_pos_changed or track_changed:
# SMTC updated - store new baseline
_position_cache["track_id"] = track_id
_position_cache["last_smtc_pos"] = smtc_pos
_position_cache["base_position"] = smtc_pos
_position_cache["base_time"] = current_time
_position_cache["is_playing"] = is_playing
pos = smtc_pos
elif is_playing:
# Interpolate position based on elapsed time
elapsed = current_time - _position_cache.get("base_time", current_time)
pos = _position_cache.get("base_position", smtc_pos) + elapsed
else:
# Paused - use base position
pos = _position_cache.get("base_position", smtc_pos)
# Update playing state
if _position_cache.get("is_playing") != is_playing:
_position_cache["base_position"] = pos if is_playing else _position_cache.get("base_position", smtc_pos)
_position_cache["base_time"] = current_time
_position_cache["is_playing"] = is_playing
# Sanity check: position should be non-negative and <= duration
if pos >= 0:
if result["duration"] and pos <= result["duration"]:
result["position"] = pos
elif result["duration"] and pos > result["duration"]:
result["position"] = result["duration"]
elif not result["duration"]:
result["position"] = pos
# Sanity check: position should be non-negative and <= duration
if pos >= 0:
if result["duration"] and pos <= result["duration"]:
result["position"] = pos
elif result["duration"] and pos > result["duration"]:
result["position"] = result["duration"]
elif not result["duration"]:
result["position"] = pos
logger.debug(f"Timeline: duration={result['duration']}, position={result['position']}")
except Exception as e:
@@ -483,6 +488,11 @@ def _sync_seek(position: float) -> bool:
return False
def shutdown_executor() -> None:
"""Shut down the WinRT thread pool executor."""
_executor.shutdown(wait=False)
class WindowsMediaController(MediaController):
"""Media controller for Windows using WinRT and pycaw."""
@@ -602,10 +612,10 @@ class WindowsMediaController(MediaController):
result = await self._run_command("next")
if result:
# Set flag to force position to 0 until title changes
_track_skip_pending["active"] = True
_track_skip_pending["old_title"] = old_title
_track_skip_pending["skip_time"] = _time.time()
with _position_lock:
_track_skip_pending["active"] = True
_track_skip_pending["old_title"] = old_title
_track_skip_pending["skip_time"] = _time.time()
logger.debug(f"Track skip initiated, old title: {old_title}")
return result
@@ -620,10 +630,10 @@ class WindowsMediaController(MediaController):
result = await self._run_command("previous")
if result:
# Set flag to force position to 0 until title changes
_track_skip_pending["active"] = True
_track_skip_pending["old_title"] = old_title
_track_skip_pending["skip_time"] = _time.time()
with _position_lock:
_track_skip_pending["active"] = True
_track_skip_pending["old_title"] = old_title
_track_skip_pending["skip_time"] = _time.time()
logger.debug(f"Track skip initiated, old title: {old_title}")
return result