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()
# 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:
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)
item_id=prov_mapping.item_id,
fully_played=False,
position=0,
+ media_item=media_item,
)
)
# also update playcount in library table
item_to_report.media_item,
fully_played=fully_played,
seconds_played=seconds_played,
+ is_playing=is_playing,
)
)
# signal 'media item played' event,
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.
- 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:
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.
- 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.
)
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
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.
- 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)
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
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.
AudioFormat,
ItemMapping,
MediaItemImage,
+ MediaItemType,
Playlist,
Podcast,
PodcastEpisode,
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.
- 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.