From: Marcel van der Veldt Date: Fri, 17 Mar 2023 09:33:59 +0000 (+0100) Subject: Fix image of queue item if source is file based (#541) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=8c18d55fe2b5fa11005dcdf460da60ab51633fc0;p=music-assistant-server.git Fix image of queue item if source is file based (#541) Resolve queue item image url (use image proxy) so players can also display embedded covers --- diff --git a/music_assistant/common/models/queue_item.py b/music_assistant/common/models/queue_item.py index 6b96d9f9..ce89d4e3 100644 --- a/music_assistant/common/models/queue_item.py +++ b/music_assistant/common/models/queue_item.py @@ -2,13 +2,16 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any +from typing import TYPE_CHECKING, Any from uuid import uuid4 from mashumaro import DataClassDictMixin from .enums import MediaType -from .media_items import ItemMapping, MediaItemImage, Radio, StreamDetails, Track +from .media_items import ItemMapping, Radio, StreamDetails, Track + +if TYPE_CHECKING: + from music_assistant.server import MusicAssistant @dataclass @@ -23,7 +26,7 @@ class QueueItem(DataClassDictMixin): sort_index: int = 0 streamdetails: StreamDetails | None = None media_item: Track | Radio | None = None - image: MediaItemImage | None = None + image_url: str | None = None def __post_init__(self): """Set default values.""" @@ -71,5 +74,14 @@ class QueueItem(DataClassDictMixin): name=name, duration=media_item.duration, media_item=media_item, - image=media_item.image, + ) + + async def resolve_image_url(self, mass: MusicAssistant) -> None: + """Resolve Image URL for the MediaItem.""" + if self.image_url: + return + if not self.media_item: + return + self.image_url = await mass.metadata.get_image_url_for_item( + self.media_item, resolve_local=True ) diff --git a/music_assistant/server/controllers/metadata.py b/music_assistant/server/controllers/metadata.py index 61f90e57..570a48a8 100755 --- a/music_assistant/server/controllers/metadata.py +++ b/music_assistant/server/controllers/metadata.py @@ -249,8 +249,6 @@ class MetaDataController: img_path = await self.get_image_url_for_item( media_item=media_item, img_type=img_type, - allow_local=True, - local_as_base64=False, ) if not img_path: return None @@ -260,8 +258,8 @@ class MetaDataController: self, media_item: MediaItemType, img_type: ImageType = ImageType.THUMB, + resolve_local: bool = True, allow_local: bool = True, - local_as_base64: bool = False, ) -> str | None: """Get url to image for given media media_item.""" if not media_item: @@ -274,29 +272,25 @@ class MetaDataController: continue if img.is_file and not allow_local: continue - if img.is_file and local_as_base64: - # return base64 string of the image (compatible with browsers) - return await self.get_thumbnail(img.url, base64=True) + if img.is_file and resolve_local: + # return imageproxy url for local filesystem items + # the original path is double encoded + encoded_url = urllib.parse.quote(urllib.parse.quote(img.url)) + return f"{self.mass.base_url}/imageproxy?path={encoded_url}" return img.url # retry with track's album if media_item.media_type == MediaType.TRACK and media_item.album: - return await self.get_image_url_for_item( - media_item.album, img_type, allow_local, local_as_base64 - ) + return await self.get_image_url_for_item(media_item.album, img_type, resolve_local) # try artist instead for albums if media_item.media_type == MediaType.ALBUM and media_item.artist: - return await self.get_image_url_for_item( - media_item.artist, img_type, allow_local, local_as_base64 - ) + return await self.get_image_url_for_item(media_item.artist, img_type, resolve_local) # last resort: track artist(s) if media_item.media_type == MediaType.TRACK and media_item.artists: for artist in media_item.artists: - return await self.get_image_url_for_item( - artist, img_type, allow_local, local_as_base64 - ) + return await self.get_image_url_for_item(artist, img_type, resolve_local) return None diff --git a/music_assistant/server/controllers/player_queues.py b/music_assistant/server/controllers/player_queues.py index 8b7e2630..b00c23ce 100755 --- a/music_assistant/server/controllers/player_queues.py +++ b/music_assistant/server/controllers/player_queues.py @@ -15,19 +15,11 @@ from music_assistant.common.models.enums import ( QueueOption, RepeatMode, ) -from music_assistant.common.models.errors import ( - MediaNotFoundError, - MusicAssistantError, - QueueEmpty, -) +from music_assistant.common.models.errors import MediaNotFoundError, MusicAssistantError, QueueEmpty from music_assistant.common.models.media_items import MediaItemType, media_from_dict from music_assistant.common.models.player_queue import PlayerQueue from music_assistant.common.models.queue_item import QueueItem -from music_assistant.constants import ( - CONF_FLOW_MODE, - FALLBACK_DURATION, - ROOT_LOGGER_NAME, -) +from music_assistant.constants import CONF_FLOW_MODE, FALLBACK_DURATION, ROOT_LOGGER_NAME from music_assistant.server.helpers.api import api_command if TYPE_CHECKING: @@ -469,6 +461,8 @@ class PlayerQueuesController: player_prov = self.mass.players.get_player_provider(queue_id) flow_mode = self.mass.config.get_player_config_value(queue.queue_id, CONF_FLOW_MODE) queue.flow_mode = flow_mode.value + # make sure that the queue item image is resolved + await queue_item.resolve_image_url(self.mass) await player_prov.cmd_play_media( queue_id, queue_item=queue_item, @@ -587,7 +581,7 @@ class PlayerQueuesController: self._queues.pop(player_id, None) self._queue_items.pop(player_id, None) - def player_ready_for_next_track( + async def player_ready_for_next_track( self, queue_or_player_id: str, current_item_id: str | None = None ) -> tuple[QueueItem, bool]: """Call when a player is ready to load the next track into the buffer. @@ -609,6 +603,8 @@ class PlayerQueuesController: next_item = self.get_item(queue.queue_id, next_index) if not next_item: raise QueueEmpty("No more tracks left in the queue.") + # make sure that the queue item image is resolved + await next_item.resolve_image_url(self.mass) queue.index_in_buffer = next_index # work out crossfade crossfade = queue.crossfade_enabled diff --git a/music_assistant/server/controllers/streams.py b/music_assistant/server/controllers/streams.py index 8f0c559f..716ae3f9 100644 --- a/music_assistant/server/controllers/streams.py +++ b/music_assistant/server/controllers/streams.py @@ -477,7 +477,7 @@ class StreamsController: ( queue_track, use_crossfade, - ) = self.mass.players.queues.player_ready_for_next_track( + ) = await self.mass.players.queues.player_ready_for_next_track( queue_id, queue_track.queue_item_id ) except QueueEmpty: diff --git a/music_assistant/server/helpers/didl_lite.py b/music_assistant/server/helpers/didl_lite.py index ece34b69..7c1f5149 100644 --- a/music_assistant/server/helpers/didl_lite.py +++ b/music_assistant/server/helpers/didl_lite.py @@ -31,14 +31,15 @@ def create_didl_metadata(url: str, queue_item: QueueItem, flow_mode: bool = Fals "" "" ) + if is_radio: # radio or other non-track item - image = queue_item.image.url if queue_item.image else "" + image = queue_item.image_url if queue_item.image else "" return ( '' f'' f"{_escape_str(queue_item.name)}" - f"{queue_item.image.url}" + f"{queue_item.image_url}" f"{image}" "object.item.audioItem.audioBroadcast" f"audio/{ext}" @@ -55,7 +56,6 @@ def create_didl_metadata(url: str, queue_item: QueueItem, flow_mode: bool = Fals album = _escape_str(queue_item.media_item.album.name) else: album = "" - image = queue_item.image.url if queue_item.image else "" item_class = "object.item.audioItem.musicTrack" duration_str = str(datetime.timedelta(seconds=queue_item.duration)) return ( @@ -68,7 +68,7 @@ def create_didl_metadata(url: str, queue_item: QueueItem, flow_mode: bool = Fals f"{queue_item.duration}" "Music Assistant" f"{queue_item.queue_item_id}" - f"{image}" + f"{queue_item.image_url}" f"{item_class}" f"audio/{ext}" f'{url}' diff --git a/music_assistant/server/providers/chromecast/__init__.py b/music_assistant/server/providers/chromecast/__init__.py index d9523937..6bc85ffc 100644 --- a/music_assistant/server/providers/chromecast/__init__.py +++ b/music_assistant/server/providers/chromecast/__init__.py @@ -408,7 +408,7 @@ class ChromecastProvider(PlayerProvider): if not current_queue_item_id: return # guard try: - next_item, crossfade = self.mass.players.queues.player_ready_for_next_track( + next_item, crossfade = await self.mass.players.queues.player_ready_for_next_track( castplayer.player_id, current_queue_item_id ) except QueueEmpty: @@ -483,14 +483,14 @@ class ChromecastProvider(PlayerProvider): "songName": queue_item.media_item.name, "artist": queue_item.media_item.artist.name if queue_item.media_item.artist else "", "title": queue_item.name, - "images": [{"url": queue_item.image.url}] if queue_item.image else None, + "images": [{"url": queue_item.image_url}] if queue_item.image_url else None, } else: stream_type = STREAM_TYPE_LIVE metadata = { "metadataType": 0, "title": queue_item.name, - "images": [{"url": queue_item.image.url}] if queue_item.image else None, + "images": [{"url": queue_item.image_url}] if queue_item.image_url else None, } return { "autoplay": True, diff --git a/music_assistant/server/providers/dlna/__init__.py b/music_assistant/server/providers/dlna/__init__.py index 789464f4..60500a31 100644 --- a/music_assistant/server/providers/dlna/__init__.py +++ b/music_assistant/server/providers/dlna/__init__.py @@ -504,7 +504,7 @@ class DLNAPlayerProvider(PlayerProvider): if not self.mass.players.queues.get_item(dlna_player.udn, current_queue_item_id): return # guard try: - next_item, crossfade = self.mass.players.queues.player_ready_for_next_track( + next_item, crossfade = await self.mass.players.queues.player_ready_for_next_track( dlna_player.udn, current_queue_item_id ) except QueueEmpty: diff --git a/music_assistant/server/providers/json_rpc/models.py b/music_assistant/server/providers/json_rpc/models.py index 19fb8c8d..8bfe882e 100644 --- a/music_assistant/server/providers/json_rpc/models.py +++ b/music_assistant/server/providers/json_rpc/models.py @@ -147,7 +147,7 @@ def playlist_item_from_mass(queue_item: QueueItem, index: int = 0) -> PlaylistIt "genre": "", "remote": 0, "remote_title": queue_item.streamdetails.stream_title if queue_item.streamdetails else "", - "artwork_url": queue_item.image.url if queue_item.image else "", + "artwork_url": queue_item.image_url or "", "bitrate": "", "duration": queue_item.duration or 0, "coverid": "-94099753136392", diff --git a/music_assistant/server/providers/slimproto/__init__.py b/music_assistant/server/providers/slimproto/__init__.py index 67585d3a..09ae5698 100644 --- a/music_assistant/server/providers/slimproto/__init__.py +++ b/music_assistant/server/providers/slimproto/__init__.py @@ -474,7 +474,7 @@ class SlimprotoProvider(PlayerProvider): if not client.current_metadata: return try: - next_item, crossfade = self.mass.players.queues.player_ready_for_next_track( + next_item, crossfade = await self.mass.players.queues.player_ready_for_next_track( client.player_id, client.current_metadata["item_id"] ) await self._handle_play_media(client, next_item, send_flush=False, crossfade=crossfade) diff --git a/music_assistant/server/providers/sonos/__init__.py b/music_assistant/server/providers/sonos/__init__.py index 0950113a..73dcf60e 100644 --- a/music_assistant/server/providers/sonos/__init__.py +++ b/music_assistant/server/providers/sonos/__init__.py @@ -504,7 +504,7 @@ class SonosPlayerProvider(PlayerProvider): if not self.mass.players.queues.get_item(sonos_player.player_id, current_queue_item_id): return # guard try: - next_item, crossfade = self.mass.players.queues.player_ready_for_next_track( + next_item, crossfade = await self.mass.players.queues.player_ready_for_next_track( sonos_player.player_id, current_queue_item_id ) except QueueEmpty: