From 699ca44260f6c790f69976d5a3d8728ce160b122 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sat, 21 Mar 2020 20:17:00 +0100 Subject: [PATCH] Clean up Sonos attributes for radio streams (#33063) --- CODEOWNERS | 1 + homeassistant/components/sonos/manifest.json | 4 +- .../components/sonos/media_player.py | 122 +++++------------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 34 insertions(+), 97 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9adf5110b4f..f0efe63ada2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -336,6 +336,7 @@ homeassistant/components/solax/* @squishykid homeassistant/components/soma/* @ratsept homeassistant/components/somfy/* @tetienne homeassistant/components/songpal/* @rytilahti +homeassistant/components/sonos/* @amelchio homeassistant/components/spaceapi/* @fabaff homeassistant/components/speedtestdotnet/* @rohankapoorcom homeassistant/components/spider/* @peternijssen diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index 74a8dea06d4..a015e7a5095 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,12 +3,12 @@ "name": "Sonos", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sonos", - "requirements": ["pysonos==0.0.24"], + "requirements": ["pysonos==0.0.25"], "dependencies": [], "ssdp": [ { "st": "urn:schemas-upnp-org:device:ZonePlayer:1" } ], - "codeowners": [] + "codeowners": ["@amelchio"] } diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 8828c27e9c7..46e3765ad44 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -4,12 +4,12 @@ import datetime import functools as ft import logging import socket -import urllib import async_timeout import pysonos from pysonos import alarms from pysonos.exceptions import SoCoException, SoCoUPnPException +import pysonos.music_library import pysonos.snapshot import voluptuous as vol @@ -338,19 +338,6 @@ def _timespan_secs(timespan): return sum(60 ** x[0] * int(x[1]) for x in enumerate(reversed(timespan.split(":")))) -def _is_radio_uri(uri): - """Return whether the URI is a stream (not a playlist).""" - radio_schemes = ( - "x-rincon-mp3radio:", - "x-sonosapi-stream:", - "x-sonosapi-radio:", - "x-sonosapi-hls:", - "hls-radio:", - "x-rincon-stream:", - ) - return uri.startswith(radio_schemes) - - class SonosEntity(MediaPlayerDevice): """Representation of a Sonos entity.""" @@ -515,17 +502,6 @@ class SonosEntity(MediaPlayerDevice): # Skip unknown types _LOGGER.error("Unhandled favorite '%s': %s", fav.title, ex) - def _radio_artwork(self, url): - """Return the private URL with artwork for a radio stream.""" - if url in UNAVAILABLE_VALUES: - return None - - if url.find("tts_proxy") > 0: - # If the content is a tts don't try to fetch an image from it. - return None - - return f"http://{self.soco.ip_address}:1400/getaa?s=1&u={urllib.parse.quote(url, safe='')}" - def _attach_player(self): """Get basic information and add event subscriptions.""" try: @@ -576,6 +552,14 @@ class SonosEntity(MediaPlayerDevice): self._shuffle = self.soco.shuffle self._uri = None + self._media_duration = None + self._media_position = None + self._media_position_updated_at = None + self._media_image_url = None + self._media_artist = None + self._media_album_name = None + self._media_title = None + self._source_name = None update_position = new_status != self._status self._status = new_status @@ -587,8 +571,11 @@ class SonosEntity(MediaPlayerDevice): else: track_info = self.soco.get_current_track_info() self._uri = track_info["uri"] + self._media_artist = track_info.get("artist") + self._media_album_name = track_info.get("album") + self._media_title = track_info.get("title") - if _is_radio_uri(track_info["uri"]): + if self.soco.is_radio_uri(track_info["uri"]): variables = event and event.variables self.update_media_radio(variables, track_info) else: @@ -604,74 +591,29 @@ class SonosEntity(MediaPlayerDevice): def update_media_linein(self, source): """Update state when playing from line-in/tv.""" - self._media_duration = None - self._media_position = None - self._media_position_updated_at = None - - self._media_image_url = None - - self._media_artist = None - self._media_album_name = None self._media_title = source - self._source_name = source def update_media_radio(self, variables, track_info): """Update state when streaming radio.""" - self._media_duration = None - self._media_position = None - self._media_position_updated_at = None + try: + library = pysonos.music_library.MusicLibrary(self.soco) + album_art_uri = variables["current_track_meta_data"].album_art_uri + self._media_image_url = library.build_album_art_full_uri(album_art_uri) + except (TypeError, KeyError, AttributeError): + pass - media_info = self.soco.avTransport.GetMediaInfo([("InstanceID", 0)]) - self._media_image_url = self._radio_artwork(media_info["CurrentURI"]) - - self._media_artist = track_info.get("artist") - self._media_album_name = None - self._media_title = track_info.get("title") - - if self._media_artist and self._media_title: - # artist and album name are in the data, concatenate - # that do display as artist. - # "Information" field in the sonos pc app - self._media_artist = "{artist} - {title}".format( - artist=self._media_artist, title=self._media_title - ) - elif variables: - # "On Now" field in the sonos pc app - current_track_metadata = variables.get("current_track_meta_data") - if current_track_metadata: - self._media_artist = current_track_metadata.radio_show.split(",")[0] - - # For radio streams we set the radio station name as the title. - current_uri_metadata = media_info["CurrentURIMetaData"] - if current_uri_metadata not in UNAVAILABLE_VALUES: - # currently soco does not have an API for this - current_uri_metadata = pysonos.xml.XML.fromstring( - pysonos.utils.really_utf8(current_uri_metadata) - ) - - md_title = current_uri_metadata.findtext( - ".//{http://purl.org/dc/elements/1.1/}title" - ) - - if md_title not in UNAVAILABLE_VALUES: - self._media_title = md_title - - if self._media_artist and self._media_title: - # some radio stations put their name into the artist - # name, e.g.: - # media_title = "Station" - # media_artist = "Station - Artist - Title" - # detect this case and trim from the front of - # media_artist for cosmetics - trim = f"{self._media_title} - " - chars = min(len(self._media_artist), len(trim)) - - if self._media_artist[:chars].upper() == trim[:chars].upper(): - self._media_artist = self._media_artist[chars:] + # Radios without tagging can have the radio URI as title. Non-playing + # radios will not have a current title. In these cases we try to use + # the radio name instead. + try: + if self.soco.is_radio_uri(self._media_title) or self.state != STATE_PLAYING: + self._media_title = variables["enqueued_transport_uri_meta_data"].title + except (TypeError, KeyError, AttributeError): + pass # Check if currently playing radio station is in favorites - self._source_name = None + media_info = self.soco.avTransport.GetMediaInfo([("InstanceID", 0)]) for fav in self._favorites: if fav.reference.get_uri() == media_info["CurrentURI"]: self._source_name = fav.title @@ -710,12 +652,6 @@ class SonosEntity(MediaPlayerDevice): self._media_image_url = track_info.get("album_art") - self._media_artist = track_info.get("artist") - self._media_album_name = track_info.get("album") - self._media_title = track_info.get("title") - - self._source_name = None - def update_volume(self, event=None): """Update information about currently volume settings.""" if event: @@ -936,7 +872,7 @@ class SonosEntity(MediaPlayerDevice): if len(fav) == 1: src = fav.pop() uri = src.reference.get_uri() - if _is_radio_uri(uri): + if self.soco.is_radio_uri(uri): self.soco.play_uri(uri, title=source) else: self.soco.clear_queue() diff --git a/requirements_all.txt b/requirements_all.txt index 602fa0a59bf..8cde5e26c92 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1543,7 +1543,7 @@ pysnmp==4.4.12 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.24 +pysonos==0.0.25 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4b3aa5878a2..956d88402c1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -583,7 +583,7 @@ pysmartthings==0.7.0 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.24 +pysonos==0.0.25 # homeassistant.components.spc pyspcwebgw==0.4.0