Skip to content

Commit 090c9c9

Browse files
Add secondary script that handles volume changes much cleaner, remove most of the changes to the metadata script
1 parent f57e5cd commit 090c9c9

File tree

2 files changed

+128
-13
lines changed

2 files changed

+128
-13
lines changed

streams/spot_connect_meta.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import requests
1313
from websockets.exceptions import ConnectionClosed, InvalidHandshake
1414
from websockets.sync.client import connect
15-
# from ..amplipi import tasks
1615

1716

1817
@dataclass
@@ -156,15 +155,6 @@ def from_json(parent: "SpotifyMetadataReader", json_data: dict) -> "Event":
156155
e.data = SeekChange(**json_data["data"])
157156
elif e.event_type == "volume":
158157
e.data = VolumeChange(**json_data["data"])
159-
volume = e.data.value
160-
if volume != parent.last_volume:
161-
url = f"http://localhost:{parent._api_port}" # TODO: Implement spotify-side volume bar to reflect the source volume bar's position
162-
# tasks.post.delay(url + 'volume', data={'volume': int(volume * 100)})
163-
delta = float((volume - parent.last_volume) / 100)
164-
vol_change = requests.patch("http://localhost/api/zones", json={"zones": [0, 1, 2, 3, 4, 5], "update": {"vol_delta_f": delta}}, timeout=5) # TODO: set zone list correctly
165-
if vol_change.ok:
166-
parent.last_volume = volume # update last_volume for future syncs
167-
168158
elif e.event_type in ["shuffle_context", "repeat_context", "repeat_track"]:
169159
e.data = ValueChange(**json_data["data"])
170160
return e
@@ -176,11 +166,9 @@ def __init__(self, url, metadata_file, debug=False):
176166
self.url: str = url
177167
self.metadata_file: str = metadata_file
178168
self.debug: bool = debug
179-
180-
self.last_volume: float = 0
181169
self._api_port: int = 3678 + int([char for char in metadata_file if char.isdigit()][0])
182170

183-
def read_metadata(self) -> Optional[Track]:
171+
def read_metadata(self) -> Optional[Status]:
184172
"""
185173
Reads metadata from the given URL and writes it to the specified metadata file.
186174
If the metadata file already exists, it will be overwritten.

streams/spotify_volume_handler.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
2+
import argparse
3+
from time import sleep
4+
import requests
5+
6+
7+
class SpotifyData:
8+
9+
def __init__(self, api_port, debug=False):
10+
self.api_port: int = api_port
11+
self.debug = debug
12+
13+
self.status: dict = self.get_status()
14+
self.last_volume: int = self.get_volume()
15+
16+
def get_status(self):
17+
self.status = requests.get(f"http://localhost:{self.api_port}/status", timeout=5).json()
18+
19+
def get_volume(self): # Not terribly useful, but it's nice to have parity with the AmpliPiData object
20+
if self.status:
21+
if self.debug:
22+
print(f"Got Spotify volume: {self.status['volume']}")
23+
return self.status["volume"]
24+
25+
26+
class AmpliPiData:
27+
28+
def __init__(self, source_id, debug=False):
29+
self.source_id = source_id
30+
self.debug = debug
31+
32+
self.status: dict = self.get_status()
33+
self.last_volume: float = self.get_volume()
34+
35+
self.connected_zones: list = []
36+
37+
def get_status(self):
38+
self.status = requests.get("http://localhost/api", timeout=5).json()
39+
40+
self.connected_zones = [zone for zone in self.status["zones"] if zone["source_id"] == self.source_id]
41+
42+
def get_volume(self):
43+
if self.connected_zones:
44+
total_vol_f = 0
45+
for zone in self.connected_zones:
46+
total_vol_f += zone["vol_f"]
47+
if self.debug:
48+
print(f"Got AmpliPi volume: {total_vol_f / len(self.connected_zones)}")
49+
return total_vol_f / len(self.connected_zones)
50+
51+
52+
class SpotifyVolumeHandler:
53+
54+
def __init__(self, port, debug=False):
55+
self.amplipi = AmpliPiData(port - 3679, debug)
56+
self.spotify = SpotifyData(port, debug)
57+
self.debug: bool = debug
58+
59+
def update_amplipi_volume(self, volume):
60+
"""Update AmpliPi's volume via the Spotify client volume slider"""
61+
delta = float((volume - self.spotify.last_volume) / 100)
62+
requests.patch("http://localhost/api/zones", json={"zones": [zone["id"] for zone in self.amplipi.connected_zones], "update": {"vol_delta_f": delta, "mute": False}}, timeout=5)
63+
self.spotify.last_volume = volume
64+
65+
def update_spotify_volume(self, volume):
66+
"""Update the Spotify client's volume slider position based on the averaged volume of all connected zones in AmpliPi"""
67+
if self.debug:
68+
print(f"Spotify vol updated: {volume}")
69+
url = f"http://localhost:{self.spotify.api_port}"
70+
new_vol = int(volume * 100)
71+
print(f"vol: {volume}, last_vol: {self.amplipi.last_volume}")
72+
requests.post(url + '/player/volume', json={"volume": new_vol}, timeout=5)
73+
self.amplipi.last_volume = volume
74+
self.spotify.last_volume = new_vol
75+
76+
def get_statuses(self):
77+
self.amplipi.get_status()
78+
if self.amplipi.last_volume is None:
79+
self.amplipi.last_volume = self.amplipi.get_volume()
80+
81+
self.spotify.get_status()
82+
if self.spotify.last_volume is None:
83+
self.spotify.last_volume = self.spotify.get_volume()
84+
85+
def handle_volumes(self):
86+
while True:
87+
try:
88+
self.get_statuses()
89+
amplipi_volume = self.amplipi.get_volume()
90+
spotify_volume = self.spotify.get_volume()
91+
92+
if self.debug:
93+
print(f"amplipi: {amplipi_volume}, last: {self.amplipi.last_volume}")
94+
print(f"spotify: {spotify_volume}, last: {self.spotify.last_volume}")
95+
print("\n\n\n")
96+
if spotify_volume != self.spotify.last_volume:
97+
if self.debug:
98+
print("Updating spotify vol...")
99+
self.update_amplipi_volume(spotify_volume)
100+
elif amplipi_volume != self.amplipi.last_volume or int(amplipi_volume * 100) != spotify_volume:
101+
if self.debug:
102+
print("Updating amplipi vol...")
103+
self.update_spotify_volume(amplipi_volume)
104+
except Exception as e:
105+
print(f"ERROR: {e}")
106+
sleep(2)
107+
108+
109+
if __name__ == "__main__":
110+
111+
parser = argparse.ArgumentParser(description="Read metadata from a given URL and write it to a file.")
112+
113+
parser.add_argument("port", help="The port that go-librespot is running on", type=int)
114+
parser.add_argument("--debug", action="store_true", help="Enable debug output")
115+
116+
args = parser.parse_args()
117+
118+
while (True):
119+
try:
120+
SpotifyVolumeHandler(args.port, args.debug).handle_volumes()
121+
except (KeyboardInterrupt, SystemExit):
122+
print("Exiting...")
123+
break
124+
except Exception as e:
125+
print(f"Error: {e}")
126+
sleep(5)
127+
continue

0 commit comments

Comments
 (0)