Skip to content

fix: MA+YTMusic playback regression β€” restore timeout tolerance and add title verification (#345)#497

Open
Scribblerman wants to merge 2 commits intomholzi:mainfrom
Scribblerman:fix/ma-playback-regression-345
Open

fix: MA+YTMusic playback regression β€” restore timeout tolerance and add title verification (#345)#497
Scribblerman wants to merge 2 commits intomholzi:mainfrom
Scribblerman:fix/ma-playback-regression-345

Conversation

@Scribblerman
Copy link
Copy Markdown

Summary

Fixes a regression introduced by #418 that reverted the MA+YTMusic playback fix from #345.

  • Timeout return value: [Code Review: bugs] Music Assistant playback always returns True masking failuresΒ #418 changed return True β†’ return False on MA playback timeout, causing start_round() to retry with a new song. Since blocking=False, the previous song is still buffering β€” the Sonos receives 3-4 songs in rapid succession.
  • Title verification: The polling loop only checked "did the title change?" but not "is it the correct song?". On retry, a late-arriving previous song was falsely confirmed as the new one (race condition).
  • Next Round button: The "Next Round" button re-enabled after a hardcoded 2s timeout instead of waiting for the server's phase change, allowing premature clicks.
  • WebSocket race condition: checkGameStatus() (async) and initAll() could both open a WebSocket simultaneously, causing InvalidStateError: Still in CONNECTING state and dropping the connection.

Changes

services/media_player.py

  • Restore return True on timeout β€” MA+YTMusic can take >8s to buffer, don't skip the song
  • Replace title_changed (any change) with title_matches (expected title substring, case-insensitive) to prevent race condition where a previous slow song is falsely confirmed

www/js/player-game.js

  • Remove hardcoded setTimeout(2000) that re-enabled the Next Round button
  • Export resetNextRoundPending() β€” called on actual phase change instead

www/js/player-core.js

  • Import and call resetNextRoundPending() when phase transitions to PLAYING
  • Add WebSocket guard in connectWithSession() and connectWebSocket() to prevent double connections

www/js/player.bundle.min.js

  • Rebuilt via npx esbuild with above JS changes

tests/unit/test_media_player.py

Test plan

  • 278 unit tests passing (2 updated, 2 new)
  • Tested live with MA + Sonos + YouTube Music
  • Verified via HA logs: no more song switching, connections=1 on REVEAL broadcast
  • Verified Next Round button stays disabled until server confirms phase change
  • No diff between v2.9.1 and main for all changed files (except +10 unrelated lines in player-game.js)

πŸ€– Generated with Claude Code

…dd title verification (mholzi#345)

- Restore return True on MA playback timeout (reverts mholzi#418 regression)
- Replace "any title change" with expected title substring match to prevent race condition
- Remove hardcoded 2s setTimeout on Next Round button, wait for server phase change
- Add WebSocket guard to prevent double connections (InvalidStateError fix)
- Update and add unit tests for title matching and timeout behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves the reliability of song playback confirmation in the media_player service by implementing case-insensitive substring matching for titles and preventing unnecessary retries during buffering. In the frontend, it adds guards against duplicate WebSocket connections and refactors the "Next Round" button logic to wait for server confirmation rather than using a fixed timeout. Review feedback highlights a potential AttributeError in the Python service if the song title is explicitly None and notes that the frontend resetNextRoundPending function needs to explicitly re-enable the UI button and reset its text to avoid it being stuck in a "Loading" state.

@Scribblerman
Copy link
Copy Markdown
Author

Note: The two failing checks (Hassfest Validation, Lint) are pre-existing on main β€” see 2243a34. This PR does not introduce any new failures.

- Use `song.get("title") or ""` to handle explicit None values (prevents
  AttributeError on .lower())
- Reset button visual state (disabled + text) in resetNextRoundPending()
  as defensive measure β€” updateRevealView() already handles this on each
  REVEAL phase, but explicit reset ensures consistency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants