diff --git a/app.py b/app.py index 55d2665..1b4af16 100644 --- a/app.py +++ b/app.py @@ -217,10 +217,20 @@ def _spotify_release(key: str, account: str = None) -> dict: intact. Putting the speaker on one of its own inputs does end the stream, and stopping *that* is honoured, so the room is left idle rather than sitting on a live input. + + A stereo pair is the exception: it gets the pause and nothing more. + Signing out restarts the leader's receiver, and a leader restarted that + way can come back playing alone -- the other half silent until the pair + is power-cycled. The Spotify app never signs a speaker out, which is + why it never trips over this. Left signed in, the pair is still either + button's to claim: a sign-in moves it between accounts regardless. """ - released = {"paused": _spotify_pause_on(key, account), "signed_out": False} + released = {"paused": _spotify_pause_on(key, account), "signed_out": False, "paired": False} speaker = None if _is_demo() else _find_speaker(_spotify_name(key)) if speaker is not None: + if _is_paired(speaker): + released["paired"] = True + return released speaker.reset_users() released["signed_out"] = True return released @@ -228,6 +238,16 @@ def _spotify_release(key: str, account: str = None) -> dict: return released +def _is_paired(speaker) -> bool: + """Whether the receiver is one half of a stereo pair (or any In-Room + Group) -- getInfo's groupStatus says GROUP, where a lone speaker says + NONE. One that will not say is treated as alone, as before.""" + try: + return speaker.info().get("groupStatus") == "GROUP" + except ZeroconfError: + return False + + def _release_over_heos(key: str): """Stop a room's Spotify stream without the speaker's help -- see _spotify_release for why it goes the long way round.""" diff --git a/static/app.js b/static/app.js index 297f302..e38ecf1 100644 --- a/static/app.js +++ b/static/app.js @@ -794,9 +794,13 @@ async function disconnectSpotify(button) { // The speaker not answering on the LAN is not a failure -- the room // still stops -- but it does leave the account signed in to it, and // that is the difference between "let go of" and "stopped". + // A stereo pair is only ever paused -- signing it out can leave one + // half silent -- so it staying signed in is the plan, not a failure. toast(data.signed_out ? `${data.device} let go of ${who}'s Spotify` - : `Stopped ${who}'s Spotify on ${data.device} — the speaker did not answer, so it stays signed in`, + : data.paired + ? `Paused ${who}'s Spotify on ${data.device}` + : `Stopped ${who}'s Spotify on ${data.device} — the speaker did not answer, so it stays signed in`, 'ok'); // The room stops with it, so drop the border, the transport row and the // song now rather than leaving a card that still claims to be playing;