snapcast-autoconfig
snapcast-autoconfig copied to clipboard
When clients are in an Idle group and one client's preferred stream starts, both clients play it
os: Arch linux snapclient: 0.25.0 snapserver: 0.25.0
First off, I realise this is a side-effect of how Snapcast works when removing clients from groups ;)
In this scenario:
- there are two clients
c1andc2 - each client has its own preferred stream (
s1,s2) - a "party" stream
s0is lower priority and plays to boths1ands2
To repro:
- Play
s0such thatc1andc2form a playing groupg0. - Pause
s0->c1andc2remain ing0which is Idle - Play
s1-> the group splits
Result:
c1is in groupg1playings1c2is in groupg2playings1<-- DOH!
Expected:
c2remains in groupg0which is Idle
Config:
---
server: tcp://localhost:1705
streams:
Canard:
clients:
- canard
Kitchen:
clients:
- kitchen
Party 1:
clients:
- canard
- kitchen
The result is the same even with more clients in g0. All clients end up in individual groups playing s1.
log.txt
Shorter log:
# play Party 1 stream (canard + kitchen)
, [2021-07-30T20:06:05.794634 #676586] INFO -- : MISCONFIGURED: Party 1
I, [2021-07-30T20:06:05.794842 #676586] INFO -- : Going to reconfigure group 'fb8c0e01-34ee-4526-acdf-4fc1db32957d':
Name: Canard -> Party 1
Stream: Canard -> Party 1
Clients: ["canard"] -> ["canard", "kitchen"]
I, [2021-07-30T20:06:05.794926 #676586] INFO -- : canard volume: 100 -> 100
# pause Party 1 stream
# play Canard stream ...
I, [2021-07-30T20:06:35.813048 #676586] INFO -- : MISCONFIGURED: Canard
I, [2021-07-30T20:06:35.813282 #676586] INFO -- : Going to reconfigure group 'fb8c0e01-34ee-4526-acdf-4fc1db32957d':
Name: Party 1 -> Canard
Stream: Party 1 -> Canard
Clients: ["canard", "kitchen"] -> ["canard"]
I, [2021-07-30T20:06:35.813451 #676586] INFO -- : canard volume: 100 -> 100
I, [2021-07-30T20:06:35.813592 #676586] INFO -- : kitchen volume: 100 -> 100
# pause Canard stream
snapserver control stream (concatenated into a json array):
[
{
"jsonrpc": "2.0",
"method": "Stream.OnUpdate",
"params": {
"id": "Party 1",
"stream": {
"id": "Party 1",
"meta": { "STREAM": "Party 1" },
"status": "playing",
"uri": {
"fragment": "",
"host": "",
"path": "/librespot",
"query": {
"bitrate": "160",
"chunk_ms": "20",
"codec": "flac",
"devicename": "Party 1",
"name": "Party 1",
"sampleformat": "44100:16:2"
},
"raw": "librespot:////librespot?bitrate=160&chunk_ms=20&codec=flac&devicename=Party 1&name=Party 1&sampleformat=44100:16:2",
"scheme": "librespot"
}
}
}
},
{ "jsonrpc": "2.0", "method": "Group.OnStreamChanged", "params": { "id": "130666ad-cbb1-a433-6a64-3e5d97bde579", "stream_id": "Party 1" } },
{
"jsonrpc": "2.0",
"method": "Server.OnUpdate",
"params": {
"server": {
"groups": [
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "",
"volume": { "muted": false, "percent": 100 }
},
"connected": false,
"host": {
"arch": "web",
"ip": "::ffff:192.168.0.666",
"mac": "66:66:66:66:66:66",
"name": "Snapweb client",
"os": "iPhone"
},
"id": "528e4925-d033-415f-95ea-ad852fd0ebcb",
"lastSeen": { "sec": 1627595355, "usec": 76512 },
"snapclient": {
"name": "Snapweb",
"protocolVersion": 2,
"version": "0.1.0"
}
}
],
"id": "6f8b43d4-d82e-d607-88f5-4daeef5d0c89",
"muted": false,
"name": "",
"stream_id": "Canard"
},
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "",
"volume": { "muted": false, "percent": 100 }
},
"connected": true,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "canard",
"lastSeen": { "sec": 1627679619, "usec": 995448 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
},
{
"config": {
"instance": 1,
"latency": 0,
"name": "kitchen",
"volume": { "muted": false, "percent": 100 }
},
"connected": true,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "kitchen",
"lastSeen": { "sec": 1627679619, "usec": 995492 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
}
],
"id": "130666ad-cbb1-a433-6a64-3e5d97bde579",
"muted": false,
"name": "Canard",
"stream_id": "Party 1"
},
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "bedroom-mark",
"volume": { "muted": false, "percent": 100 }
},
"connected": false,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "bedroom-mark",
"lastSeen": { "sec": 1627679600, "usec": 207626 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
}
],
"id": "8a9f1bf6-313b-4510-b238-5f3587ba9447",
"muted": false,
"name": "",
"stream_id": "Canard"
}
],
"server": {
"host": {
"arch": "x86_64",
"ip": "",
"mac": "",
"name": "canard",
"os": "Arch Linux"
},
"snapserver": {
"controlProtocolVersion": 1,
"name": "Snapserver",
"protocolVersion": 1,
"version": "0.25.0"
}
},
"streams": [
{
"id": "Canard",
"meta": { "STREAM": "Canard" },
"status": "idle",
"uri": {
"fragment": "",
"host": "",
"path": "/tmp/mopidy.canard",
"query": {
"chunk_ms": "20",
"codec": "flac",
"name": "Canard",
"sampleformat": "48000:16:2"
},
"raw": "pipe:////tmp/mopidy.canard?chunk_ms=20&codec=flac&name=Canard&sampleformat=48000:16:2",
"scheme": "pipe"
}
},
{
"id": "Kitchen",
"meta": { "STREAM": "Kitchen" },
"status": "idle",
"uri": {
"fragment": "",
"host": "",
"path": "/tmp/mopidy.kitchen",
"query": {
"chunk_ms": "20",
"codec": "flac",
"name": "Kitchen",
"sampleformat": "48000:16:2"
},
"raw": "pipe:////tmp/mopidy.kitchen?chunk_ms=20&codec=flac&name=Kitchen&sampleformat=48000:16:2",
"scheme": "pipe"
}
},
{
"id": "Party 1",
"meta": { "STREAM": "Party 1" },
"status": "playing",
"uri": {
"fragment": "",
"host": "",
"path": "/librespot",
"query": {
"bitrate": "160",
"chunk_ms": "20",
"codec": "flac",
"devicename": "Party 1",
"name": "Party 1",
"sampleformat": "44100:16:2"
},
"raw": "librespot:////librespot?bitrate=160&chunk_ms=20&codec=flac&devicename=Party 1&name=Party 1&sampleformat=44100:16:2",
"scheme": "librespot"
}
}
]
}
}
},
{ "jsonrpc": "2.0", "method": "Group.OnNameChanged", "params": { "id": "130666ad-cbb1-a433-6a64-3e5d97bde579", "name": "Party 1" } },
{
"jsonrpc": "2.0",
"method": "Stream.OnUpdate",
"params": {
"id": "Party 1",
"stream": {
"id": "Party 1",
"meta": { "STREAM": "Party 1" },
"status": "idle",
"uri": {
"fragment": "",
"host": "",
"path": "/librespot",
"query": {
"bitrate": "160",
"chunk_ms": "20",
"codec": "flac",
"devicename": "Party 1",
"name": "Party 1",
"sampleformat": "44100:16:2"
},
"raw": "librespot:////librespot?bitrate=160&chunk_ms=20&codec=flac&devicename=Party 1&name=Party 1&sampleformat=44100:16:2",
"scheme": "librespot"
}
}
}
},
{
"jsonrpc": "2.0",
"method": "Stream.OnUpdate",
"params": {
"id": "Canard",
"stream": {
"id": "Canard",
"meta": { "STREAM": "Canard" },
"status": "playing",
"uri": {
"fragment": "",
"host": "",
"path": "/tmp/mopidy.canard",
"query": {
"chunk_ms": "20",
"codec": "flac",
"name": "Canard",
"sampleformat": "48000:16:2"
},
"raw": "pipe:////tmp/mopidy.canard?chunk_ms=20&codec=flac&name=Canard&sampleformat=48000:16:2",
"scheme": "pipe"
}
}
}
},
{ "jsonrpc": "2.0", "method": "Group.OnStreamChanged", "params": { "id": "130666ad-cbb1-a433-6a64-3e5d97bde579", "stream_id": "Canard" } },
{
"jsonrpc": "2.0",
"method": "Server.OnUpdate",
"params": {
"server": {
"groups": [
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "",
"volume": { "muted": false, "percent": 100 }
},
"connected": false,
"host": {
"arch": "web",
"ip": "::ffff:192.168.0.666",
"mac": "66:66:66:66:66:66",
"name": "Snapweb client",
"os": "iPhone"
},
"id": "528e4925-d033-415f-95ea-ad852fd0ebcb",
"lastSeen": { "sec": 1627595355, "usec": 76512 },
"snapclient": {
"name": "Snapweb",
"protocolVersion": 2,
"version": "0.1.0"
}
}
],
"id": "6f8b43d4-d82e-d607-88f5-4daeef5d0c89",
"muted": false,
"name": "",
"stream_id": "Canard"
},
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "",
"volume": { "muted": false, "percent": 100 }
},
"connected": true,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "canard",
"lastSeen": { "sec": 1627679648, "usec": 14893 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
}
],
"id": "130666ad-cbb1-a433-6a64-3e5d97bde579",
"muted": false,
"name": "Party 1",
"stream_id": "Canard"
},
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "bedroom-mark",
"volume": { "muted": false, "percent": 100 }
},
"connected": false,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "bedroom-mark",
"lastSeen": { "sec": 1627679600, "usec": 207626 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
}
],
"id": "8a9f1bf6-313b-4510-b238-5f3587ba9447",
"muted": false,
"name": "",
"stream_id": "Canard"
},
{
"clients": [
{
"config": {
"instance": 1,
"latency": 0,
"name": "kitchen",
"volume": { "muted": false, "percent": 100 }
},
"connected": true,
"host": {
"arch": "x86_64",
"ip": "127.0.0.1",
"mac": "66:66:66:66:66:66",
"name": "canard",
"os": "Arch Linux"
},
"id": "kitchen",
"lastSeen": { "sec": 1627679648, "usec": 14684 },
"snapclient": {
"name": "Snapclient",
"protocolVersion": 2,
"version": "0.25.0"
}
}
],
"id": "fd017fd6-37c4-a8b9-8b41-e983445598aa",
"muted": false,
"name": "",
"stream_id": "Canard"
}
],
"server": {
"host": {
"arch": "x86_64",
"ip": "",
"mac": "",
"name": "canard",
"os": "Arch Linux"
},
"snapserver": {
"controlProtocolVersion": 1,
"name": "Snapserver",
"protocolVersion": 1,
"version": "0.25.0"
}
},
"streams": [
{
"id": "Canard",
"meta": { "STREAM": "Canard" },
"status": "playing",
"uri": {
"fragment": "",
"host": "",
"path": "/tmp/mopidy.canard",
"query": {
"chunk_ms": "20",
"codec": "flac",
"name": "Canard",
"sampleformat": "48000:16:2"
},
"raw": "pipe:////tmp/mopidy.canard?chunk_ms=20&codec=flac&name=Canard&sampleformat=48000:16:2",
"scheme": "pipe"
}
},
{
"id": "Kitchen",
"meta": { "STREAM": "Kitchen" },
"status": "idle",
"uri": {
"fragment": "",
"host": "",
"path": "/tmp/mopidy.kitchen",
"query": {
"chunk_ms": "20",
"codec": "flac",
"name": "Kitchen",
"sampleformat": "48000:16:2"
},
"raw": "pipe:////tmp/mopidy.kitchen?chunk_ms=20&codec=flac&name=Kitchen&sampleformat=48000:16:2",
"scheme": "pipe"
}
},
{
"id": "Party 1",
"meta": { "STREAM": "Party 1" },
"status": "idle",
"uri": {
"fragment": "",
"host": "",
"path": "/librespot",
"query": {
"bitrate": "160",
"chunk_ms": "20",
"codec": "flac",
"devicename": "Party 1",
"name": "Party 1",
"sampleformat": "44100:16:2"
},
"raw": "librespot:////librespot?bitrate=160&chunk_ms=20&codec=flac&devicename=Party 1&name=Party 1&sampleformat=44100:16:2",
"scheme": "librespot"
}
}
]
}
}
},
{ "jsonrpc": "2.0", "method": "Group.OnNameChanged", "params": { "id": "130666ad-cbb1-a433-6a64-3e5d97bde579", "name": "Canard" } }
]
I wonder, would it be possible to catch unnamed groups that have only one client and update its stream based on config? (and name it at the same time...)
Hi @markferry - glad to see someone else finds this tool useful!
I hadn't noticed your issue yet, but I actually saw this behavior myself the other day. I added some behavior that actually just muted groups like this, to work around it - see this commit range: https://github.com/ahayworth/snapcast-autoconfig/compare/5a424a70a112552860f5cd41f5b7b8af68ff74c9...main
I wonder if that solves the problem well enough for you? If not, I can look into trying to handle it more robustly.
Thanks Andrew, just back at this now.
The commits on main seem less reliable than before.
I'll dig deeper tomorrow, but in the meantime I've noticed:
- fails to group streams when it should
- flickery muting/unmuting of one group/stream/client
Here, for example is the flickering of the party1 group:
f5340600-8e42-3a38-e2d1-f82e306976a0 (name: 'party1', stream: 'party1', muted: 'false' / playing) ["outside"]
----------
I, [2021-08-12T03:42:35.893530 #1054108] INFO -- : MISCONFIGURED: f5340600-8e42-3a38-e2d1-f82e306976a0
I, [2021-08-12T03:42:35.893565 #1054108] INFO -- : Going to mute group 'f5340600-8e42-3a38-e2d1-f82e306976a0' / 'party1' / 'party1'!
D, [2021-08-12T03:42:35.893757 #1054108] DEBUG -- : -> Group.SetMute ({:id=>"f5340600-8e42-3a38-e2d1-f82e306976a0", :mute=>true})
D, [2021-08-12T03:42:36.135021 #1054108] DEBUG -- : -> Server.GetStatus ([])
D, [2021-08-12T03:42:36.152818 #1054108] DEBUG -- : <- Server.GetStatus
D, [2021-08-12T03:42:37.894629 #1054108] DEBUG -- :
----------
cfb48486-a149-7b17-5a7e-2efd041d101c (name: 'kitchen', stream: 'kitchen', muted: 'false' / playing) ["kitchen"]
4733822b-8420-c805-71c2-c2c196835896 (name: 'ballroom', stream: 'outside', muted: 'true' / idle) ["ballroom"]
63eddb4a-cb3e-6df6-6552-383a0ebe4fb1 (name: '', stream: 'Iris', muted: 'false' / idle) ["canard"]
08429d46-fa38-3867-3c82-98b1afc0dde8 (name: '', stream: 'Iris', muted: 'false' / idle) ["33692697-bccc-43b2-bc9b-e91fca21749a"]
f5340600-8e42-3a38-e2d1-f82e306976a0 (name: 'party1', stream: 'party1', muted: 'true' / playing) ["outside"]
----------
I, [2021-08-12T03:42:37.895957 #1054108] INFO -- : MISCONFIGURED: party1
I, [2021-08-12T03:42:37.896190 #1054108] INFO -- : Going to reconfigure group 'f5340600-8e42-3a38-e2d1-f82e306976a0':
Name: party1 -> party1
Stream: party1 -> party1
Clients: ["outside"] -> ["outside"]
Muted: true -> false
I, [2021-08-12T03:42:37.896311 #1054108] INFO -- : outside volume: 100 -> 100
D, [2021-08-12T03:42:37.896806 #1054108] DEBUG -- : -> Group.SetMute ({:id=>"f5340600-8e42-3a38-e2d1-f82e306976a0", :mute=>false})
D, [2021-08-12T03:42:38.135936 #1054108] DEBUG -- : -> Server.GetStatus ([])
D, [2021-08-12T03:42:38.211380 #1054108] DEBUG -- : <- Server.GetStatus
D, [2021-08-12T03:42:39.898487 #1054108] DEBUG -- :
----------
cfb48486-a149-7b17-5a7e-2efd041d101c (name: 'kitchen', stream: 'kitchen', muted: 'false' / playing) ["kitchen"]
4733822b-8420-c805-71c2-c2c196835896 (name: 'ballroom', stream: 'outside', muted: 'true' / idle) ["ballroom"]
63eddb4a-cb3e-6df6-6552-383a0ebe4fb1 (name: '', stream: 'Iris', muted: 'false' / idle) ["canard"]
08429d46-fa38-3867-3c82-98b1afc0dde8 (name: '', stream: 'Iris', muted: 'false' / idle) ["33692697-bccc-43b2-bc9b-e91fca21749a"]
f5340600-8e42-3a38-e2d1-f82e306976a0 (name: 'party1', stream: 'party1', muted: 'false' / playing) ["outside"]
----------
I, [2021-08-12T03:42:39.899627 #1054108] INFO -- : MISCONFIGURED: f5340600-8e42-3a38-e2d1-f82e306976a0
I, [2021-08-12T03:42:39.899757 #1054108] INFO -- : Going to mute group 'f5340600-8e42-3a38-e2d1-f82e306976a0' / 'party1' / 'party1'!
D, [2021-08-12T03:42:39.900162 #1054108] DEBUG -- : -> Group.SetMute ({:id=>"f5340600-8e42-3a38-e2d1-f82e306976a0", :mute=>true})
D, [2021-08-12T03:42:40.137322 #1054108] DEBUG -- : -> Server.GetStatus ([])
D, [2021-08-12T03:42:40.212560 #1054108] DEBUG -- : <- Server.GetStatus
D, [2021-08-12T03:42:41.901234 #1054108] DEBUG -- :
----------
cfb48486-a149-7b17-5a7e-2efd041d101c (name: 'kitchen', stream: 'kitchen', muted: 'false' / playing) ["kitchen"]
4733822b-8420-c805-71c2-c2c196835896 (name: 'ballroom', stream: 'outside', muted: 'true' / idle) ["ballroom"]
63eddb4a-cb3e-6df6-6552-383a0ebe4fb1 (name: '', stream: 'Iris', muted: 'false' / idle) ["canard"]
08429d46-fa38-3867-3c82-98b1afc0dde8 (name: '', stream: 'Iris', muted: 'false' / idle) ["33692697-bccc-43b2-bc9b-e91fca21749a"]
f5340600-8e42-3a38-e2d1-f82e306976a0 (name: 'party1', stream: 'party1', muted: 'true' / playing) ["outside"]
----------
I, [2021-08-12T03:42:41.901492 #1054108] INFO -- : MISCONFIGURED: party1
I, [2021-08-12T03:42:41.901533 #1054108] INFO -- : Going to reconfigure group 'f5340600-8e42-3a38-e2d1-f82e306976a0':
Name: party1 -> party1
Stream: party1 -> party1
Clients: ["outside"] -> ["outside"]
Muted: true -> false
I, [2021-08-12T03:42:41.901561 #1054108] INFO -- : outside volume: 100 -> 100
D, [2021-08-12T03:42:41.901721 #1054108] DEBUG -- : -> Group.SetMute ({:id=>"f5340600-8e42-3a38-e2d1-f82e306976a0", :mute=>false})
D, [2021-08-12T03:42:42.139069 #1054108] DEBUG -- : -> Server.GetStatus ([])
D, [2021-08-12T03:42:42.156790 #1054108] DEBUG -- : <- Server.GetStatus
D, [2021-08-12T03:42:43.902333 #1054108] DEBUG -- :
----------
cfb48486-a149-7b17-5a7e-2efd041d101c (name: 'kitchen', stream: 'kitchen', muted: 'false' / playing) ["kitchen"]
4733822b-8420-c805-71c2-c2c196835896 (name: 'ballroom', stream: 'outside', muted: 'true' / idle) ["ballroom"]
63eddb4a-cb3e-6df6-6552-383a0ebe4fb1 (name: '', stream: 'Iris', muted: 'false' / idle) ["canard"]
08429d46-fa38-3867-3c82-98b1afc0dde8 (name: '', stream: 'Iris', muted: 'false' / idle) ["33692697-bccc-43b2-bc9b-e91fca21749a"]
f5340600-8e42-3a38-e2d1-f82e306976a0 (name: 'party1', stream: 'party1', muted: 'false' / playing) ["outside"]
----------
I, [2021-08-12T03:42:43.902885 #1054108] INFO -- : MISCONFIGURED: f5340600-8e42-3a38-e2d1-f82e306976a0
I, [2021-08-12T03:42:43.902950 #1054108] INFO -- : Going to mute group 'f5340600-8e42-3a38-e2d1-f82e306976a0' / 'party1' / 'party1'!
D, [2021-08-12T03:42:43.903239 #1054108] DEBUG -- : -> Group.SetMute ({:id=>"f5340600-8e42-3a38-e2d1-f82e306976a0", :mute=>true})
D, [2021-08-12T03:42:44.139655 #1054108] DEBUG -- : -> Server.GetStatus ([])
D, [2021-08-12T03:42:44.157686 #1054108] DEBUG -- : <- Server.GetStatus
@markferry I'm surprised, honestly - this fixed the similar problems I was seeing at my house. But, clearly, it's not the right answer. I'll dig into this a little bit tonight and see if I can't figure something out. I wrote this whole thing on some fancy new ruby async libraries and didn't write tests or anything, so there's probably horrible bugs to work out. I appreciate the report, and the patience!
(Of course, if you think you know how to fix it already, PRs are always welcome - but otherwise I'll work on this a bit tonight. 😄 )