From: Marcel van der Veldt Date: Mon, 24 Feb 2025 20:22:51 +0000 (+0100) Subject: Chore: Enhance the on_played callback with extra data X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=9b4093086b93fdd0858f9dc0806bf5ccf3cc03f4;p=music-assistant-server.git Chore: Enhance the on_played callback with extra data --- diff --git a/music_assistant/controllers/music.py b/music_assistant/controllers/music.py index 06cf3133..7fa57637 100644 --- a/music_assistant/controllers/music.py +++ b/music_assistant/controllers/music.py @@ -825,6 +825,7 @@ class MusicController(CoreController): media_item: MediaItemType, fully_played: bool = True, seconds_played: int | None = None, + is_playing: bool = False, ) -> None: """Mark item as played in playlog.""" timestamp = utc_timestamp() @@ -836,23 +837,24 @@ class MusicController(CoreController): # one-off items like TTS or some sound effect etc. return - # update generic playlog table - await self.database.insert( - DB_TABLE_PLAYLOG, - { - "item_id": media_item.item_id, - "provider": media_item.provider, - "media_type": media_item.media_type.value, - "name": media_item.name, - "image": serialize_to_json(media_item.image.to_dict()) - if media_item.image - else None, - "fully_played": fully_played, - "seconds_played": seconds_played, - "timestamp": timestamp, - }, - allow_replace=True, - ) + # update generic playlog table (when not playing) + if not is_playing: + await self.database.insert( + DB_TABLE_PLAYLOG, + { + "item_id": media_item.item_id, + "provider": media_item.provider, + "media_type": media_item.media_type.value, + "name": media_item.name, + "image": serialize_to_json(media_item.image.to_dict()) + if media_item.image + else None, + "fully_played": fully_played, + "seconds_played": seconds_played, + "timestamp": timestamp, + }, + allow_replace=True, + ) # forward to provider(s) to sync resume state (e.g. for audiobooks) for prov_mapping in media_item.provider_mappings: @@ -863,11 +865,13 @@ class MusicController(CoreController): item_id=prov_mapping.item_id, fully_played=fully_played, position=seconds_played, + is_playing=is_playing, + media_item=media_item, ) ) # also update playcount in library table (if fully played) - if not fully_played: + if not fully_played or is_playing: return if not (ctrl := self.get_controller(media_item.media_type)): # skip non media items (e.g. plugin source) @@ -912,6 +916,7 @@ class MusicController(CoreController): item_id=prov_mapping.item_id, fully_played=False, position=0, + media_item=media_item, ) ) # also update playcount in library table diff --git a/music_assistant/controllers/player_queues.py b/music_assistant/controllers/player_queues.py index e34b68df..2a55943e 100644 --- a/music_assistant/controllers/player_queues.py +++ b/music_assistant/controllers/player_queues.py @@ -1925,6 +1925,7 @@ class PlayerQueuesController(CoreController): item_to_report.media_item, fully_played=fully_played, seconds_played=seconds_played, + is_playing=is_playing, ) ) # signal 'media item played' event, diff --git a/music_assistant/models/music_provider.py b/music_assistant/models/music_provider.py index 650ff631..e1871390 100644 --- a/music_assistant/models/music_provider.py +++ b/music_assistant/models/music_provider.py @@ -359,6 +359,8 @@ class MusicProvider(Provider): item_id: str, fully_played: bool, position: int, + is_playing: bool = False, + media_item: MediaItemType | None = None, ) -> None: """ Handle callback when a (playable) media item has been played. @@ -369,9 +371,14 @@ class MusicProvider(Provider): - every 30s when a track is playing Fully played is True when the track has been played to the end. + Position is the last known position of the track in seconds, to sync resume state. When fully_played is set to false and position is 0, the user marked the item as unplayed in the UI. + + is_playing is True when the track is currently playing. + + media_item is the full media item details of the played/playing track. """ async def resolve_image(self, path: str) -> str | bytes: diff --git a/music_assistant/providers/_template_music_provider/__init__.py b/music_assistant/providers/_template_music_provider/__init__.py index b9600575..5a8f2e23 100644 --- a/music_assistant/providers/_template_music_provider/__init__.py +++ b/music_assistant/providers/_template_music_provider/__init__.py @@ -451,6 +451,8 @@ class MyDemoMusicprovider(MusicProvider): item_id: str, fully_played: bool, position: int, + is_playing: bool = False, + media_item: MediaItemType | None = None, ) -> None: """ Handle callback when a (playable) media item has been played. @@ -461,9 +463,14 @@ class MyDemoMusicprovider(MusicProvider): - every 30s when a track is playing Fully played is True when the track has been played to the end. + Position is the last known position of the track in seconds, to sync resume state. When fully_played is set to false and position is 0, the user marked the item as unplayed in the UI. + + is_playing is True when the track is currently playing. + + media_item is the full media item details of the played/playing track. """ # This is an OPTIONAL callback that is called when an item has been streamed. # You can use this e.g. for playback reporting or statistics. diff --git a/music_assistant/providers/audible/__init__.py b/music_assistant/providers/audible/__init__.py index 0f348d97..a5e8a72a 100644 --- a/music_assistant/providers/audible/__init__.py +++ b/music_assistant/providers/audible/__init__.py @@ -29,7 +29,7 @@ from music_assistant.providers.audible.audible_helper import ( ) if TYPE_CHECKING: - from music_assistant_models.media_items import Audiobook + from music_assistant_models.media_items import Audiobook, MediaItemType from music_assistant_models.provider import ProviderManifest from music_assistant_models.streamdetails import StreamDetails @@ -285,6 +285,8 @@ class Audibleprovider(MusicProvider): item_id: str, fully_played: bool, position: int, + is_playing: bool = False, + media_item: MediaItemType | None = None, ) -> None: """ Handle callback when a (playable) media item has been played. @@ -295,9 +297,14 @@ class Audibleprovider(MusicProvider): - every 30s when a track is playing Fully played is True when the track has been played to the end. + Position is the last known position of the track in seconds, to sync resume state. When fully_played is set to false and position is 0, the user marked the item as unplayed in the UI. + + is_playing is True when the track is currently playing. + + media_item is the full media item details of the played/playing track. """ await self.helper.set_last_position(item_id, position) diff --git a/music_assistant/providers/audiobookshelf/__init__.py b/music_assistant/providers/audiobookshelf/__init__.py index a760bd6b..285480ab 100644 --- a/music_assistant/providers/audiobookshelf/__init__.py +++ b/music_assistant/providers/audiobookshelf/__init__.py @@ -30,7 +30,12 @@ from music_assistant_models.enums import ( StreamType, ) from music_assistant_models.errors import LoginFailed, MediaNotFoundError -from music_assistant_models.media_items import AudioFormat, BrowseFolder, MediaItemTypeOrItemMapping +from music_assistant_models.media_items import ( + AudioFormat, + BrowseFolder, + MediaItemType, + MediaItemTypeOrItemMapping, +) from music_assistant_models.streamdetails import StreamDetails from music_assistant.helpers.ffmpeg import get_ffmpeg_stream @@ -538,7 +543,13 @@ class Audiobookshelf(MusicProvider): return False, 0 async def on_played( - self, media_type: MediaType, item_id: str, fully_played: bool, position: int + self, + media_type: MediaType, + item_id: str, + fully_played: bool, + position: int, + is_playing: bool = False, + media_item: MediaItemType | None = None, ) -> None: """Update progress in Audiobookshelf. diff --git a/music_assistant/providers/opensubsonic/sonic_provider.py b/music_assistant/providers/opensubsonic/sonic_provider.py index 731a6ab7..ac0e01e7 100644 --- a/music_assistant/providers/opensubsonic/sonic_provider.py +++ b/music_assistant/providers/opensubsonic/sonic_provider.py @@ -33,6 +33,7 @@ from music_assistant_models.media_items import ( AudioFormat, ItemMapping, MediaItemImage, + MediaItemType, Playlist, Podcast, PodcastEpisode, @@ -797,6 +798,8 @@ class OpenSonicProvider(MusicProvider): item_id: str, fully_played: bool, position: int, + is_playing: bool = False, + media_item: MediaItemType | None = None, ) -> None: """ Handle callback when a (playable) media item has been played. @@ -807,9 +810,14 @@ class OpenSonicProvider(MusicProvider): - every 30s when a track is playing Fully played is True when the track has been played to the end. + Position is the last known position of the track in seconds, to sync resume state. When fully_played is set to false and position is 0, the user marked the item as unplayed in the UI. + + is_playing is True when the track is currently playing. + + media_item is the full media item details of the played/playing track. """ # Leave this function as the place where we will create a bookmark for podcasts when they # are stopped early and delete the bookmark when they are finished.