|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +from contextlib import suppress |
5 | 6 | from typing import TYPE_CHECKING |
6 | 7 |
|
7 | 8 | from music_assistant_models.enums import EventType, MediaType |
8 | | -from music_assistant_models.errors import PlayerCommandFailed, PlayerUnavailableError |
9 | | -from music_assistant_models.helpers import create_sort_name |
10 | | -from music_assistant_models.media_items import Track |
| 9 | +from music_assistant_models.errors import ( |
| 10 | + MusicAssistantError, |
| 11 | + PlayerCommandFailed, |
| 12 | + PlayerUnavailableError, |
| 13 | +) |
11 | 14 | from music_assistant_models.player import Player |
12 | 15 |
|
13 | 16 | if TYPE_CHECKING: |
@@ -205,44 +208,69 @@ async def add_currently_playing_to_favorites(self, player_id: str) -> None: |
205 | 208 | Will raise an error if the player is not currently playing anything |
206 | 209 | or if the currently playing media can not be resolved to a media item. |
207 | 210 | """ |
| 211 | + assert self.client.server_info # for type checking |
| 212 | + if self.client.server_info.schema_version >= 27: |
| 213 | + # if the server supports the new favorites endpoint, use that |
| 214 | + await self.client.send_command( |
| 215 | + "players/add_currently_playing_to_favorites", player_id=player_id |
| 216 | + ) |
| 217 | + return |
| 218 | + # Fallback implementation for older server versions. |
| 219 | + # TODO: remove this after a while, once all/most servers are updated |
| 220 | + |
| 221 | + # guard for unknown player - which should not happen, but just in case |
208 | 222 | if not (player := self._players.get(player_id)): |
209 | 223 | raise PlayerUnavailableError(f"Player {player_id} not found") |
210 | | - if not player.active_source: |
211 | | - raise PlayerCommandFailed("Player has no active source") |
212 | | - if mass_queue := self.client.player_queues.get(player.active_source): |
| 224 | + # handle mass player queue active |
| 225 | + if mass_queue := await self.client.player_queues.get_active_queue(player_id): |
213 | 226 | if not (current_item := mass_queue.current_item) or not current_item.media_item: |
214 | 227 | raise PlayerCommandFailed("No current item to add to favorites") |
215 | 228 | # if we're playing a radio station, try to resolve the currently playing track |
216 | | - if ( |
217 | | - current_item.media_item.media_type == MediaType.RADIO |
218 | | - and (streamdetails := mass_queue.current_item.streamdetails) |
219 | | - and (stream_title := streamdetails.stream_title) |
220 | | - and " - " in stream_title |
221 | | - ): |
222 | | - search_result = await self.client.music.search( |
223 | | - search_query=stream_title, |
224 | | - media_types=[MediaType.TRACK], |
225 | | - ) |
226 | | - for search_track in search_result.tracks: |
227 | | - if not isinstance(search_track, Track): |
228 | | - continue |
229 | | - # check if the artist and title match |
230 | | - # for now we only allow a strict match on the artist and title |
231 | | - artist, title = stream_title.split(" - ", 1) |
232 | | - if create_sort_name(artist) != create_sort_name(search_track.artist_str): |
233 | | - continue |
234 | | - if create_sort_name(title) != create_sort_name(search_track.name): |
235 | | - continue |
236 | | - # we found a match, add it to the favorites |
237 | | - await self.client.music.add_item_to_favorites(search_track) |
| 229 | + if current_item.media_item.media_type == MediaType.RADIO: |
| 230 | + if not ( |
| 231 | + (streamdetails := mass_queue.current_item.streamdetails) |
| 232 | + and (stream_title := streamdetails.stream_title) |
| 233 | + and " - " in stream_title |
| 234 | + ): |
| 235 | + # no stream title available, so we can't resolve the track |
| 236 | + # this can happen if the radio station does not provide metadata |
| 237 | + # or there's a commercial break |
| 238 | + # Possible future improvement could be to actually detect the song with a |
| 239 | + # shazam-like approach. |
| 240 | + raise PlayerCommandFailed("No current item to add to favorites") |
| 241 | + # send the streamtitle into a global search query |
| 242 | + search_artist, search_title_title = stream_title.split(" - ", 1) |
| 243 | + if track := await self.client.music.get_track_by_name( |
| 244 | + search_title_title, search_artist |
| 245 | + ): |
| 246 | + # we found a track, so add it to the favorites |
| 247 | + await self.client.music.add_item_to_favorites(track) |
238 | 248 | return |
239 | | - # any other media item, just add it to the favorites |
| 249 | + # any other media item, just add it to the favorites directly |
240 | 250 | await self.client.music.add_item_to_favorites(current_item.media_item) |
241 | 251 | return |
242 | | - # handle other source active using the current_media |
243 | | - if not (current_media := player.current_media) or not current_media.uri: |
244 | | - raise PlayerCommandFailed("No current item to add to favorites") |
245 | | - await self.client.music.add_item_to_favorites(current_media.uri) |
| 252 | + # guard for player with no active source |
| 253 | + if not player.active_source: |
| 254 | + raise PlayerCommandFailed("Player has no active source") |
| 255 | + # handle other source active using the current_media with uri |
| 256 | + if current_media := player.current_media: |
| 257 | + # prefer the uri of the current media item |
| 258 | + if current_media.uri: |
| 259 | + with suppress(MusicAssistantError): |
| 260 | + await self.client.music.add_item_to_favorites(current_media.uri) |
| 261 | + return |
| 262 | + # fallback to search based on artist and title (and album if available) |
| 263 | + if current_media.artist and current_media.title: # noqa: SIM102 |
| 264 | + if track := await self.client.music.get_track_by_name( |
| 265 | + current_media.title, |
| 266 | + current_media.artist, |
| 267 | + current_media.album, |
| 268 | + ): |
| 269 | + # we found a track, so add it to the favorites |
| 270 | + await self.client.music.add_item_to_favorites(track) |
| 271 | + return |
| 272 | + # if we reach here, we could not resolve the currently playing item |
| 273 | + raise PlayerCommandFailed("No current item to add to favorites") |
246 | 274 |
|
247 | 275 | # Other endpoints/commands |
248 | 276 |
|
|
0 commit comments