media
media copied to clipboard
How to update current media metadata (user rating) without interrupting the playback?
I am using androidx.media3 to develop an audio player app which provides users with an option to rate currently playng media. So, inside my MediaSessionCallback I do this:
override fun onSetRating(
session: MediaSession,
controller: ControllerInfo,
rating: Rating
): ListenableFuture<SessionResult> {
val item = session.player.currentMediaItem
item?.let {
val metadata = it.mediaMetadata.buildUpon().setUserRating(rating).build()
val mediaItem = it.buildUpon().setMediaMetadata(metadata).build()
session.player.setMediaItem(mediaItem, false)
}
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
}
This works, but with the only caveat - it interrupts the audio for the moment. Please, help me to understand how user rating should work.
It's currently not possible to update the media item I'm afraid. I think you need to maintain these ratings in your app instead like in a map for each mediaId as a key or similar.
You need to do this anyway I think as you loose the updated media items once they are removed from the player. So at some point the rating needs to be stored. It's probably best to provide the rating in the viewModel of the UI read it and update it from there instead of the field in the media item.
Thanks for your answer.
I guess I need to give more details of what I'm trying to achive and why I need to update current metadata. When the media is playing the service posts a mediastyle notification that has a custom action with a heart icon. This icon reflects the current state of the media rating - outlined if unrated and filled if rated. When user taps on that action he changes the rating and the notification needs to be updated.
I am using MediaSessionService class that has onNotificationUpdated method. I've overrided that method to build my custom notification, but this function is triggered only when currently playing media changes, specifically 'playWhenReady' field or 'mediaMetadata' field:
if (Util.areEqual(oldPlayerInfo.mediaMetadata, newPlayerInfo.mediaMetadata)
&& oldPlayerInfo.playWhenReady == newPlayerInfo.playWhenReady) {
return;
}
updateNotificationIfNeeded(session);
So theoretically I can write my own code for that but it will not be the cleanest solution, because in that case I need to update the notification manually, for example like this:
NotificationUtil.setNotification(
applicationContext,
NOTIFICATION_ID,
onUpdateNotification(session).notification
)
This is ugly. It would be better if I could just update the current media metadata and the notification will update automatically.
P.S. I have no problem maintaining the rating outside of the service scope, this is all about updating the notification. I know I can return null inside onUpdateNotification method, but I really like automatic foreground/background transition which lets me witing less code.
Thanks for the detailed clarification. I understand your use case.
I marked this issue as an enhancement.
As a note for future implementation:
There are two things we may want to look into:
-
Apps need a way to invalidate the notification and trigger that a new notification is built and posted. This makes sure that apps can add custom actions (or information) that may change independently from the state of the player that actually triggers an auitomatic update of the notification. That's pretty much what we had with
PlayerNotificationmanager.invalidate()in the old world. -
In general we want to think of a way that app can update the metadata of the media item of the media source, that eventually is passed to
onMediaMetadataChanged(). At least for the use case describe in this issue this would be the most natural way to bring in updated metadata and just let the service/notificationManager do it's usual task with the new metadata.
Point 2 is a bit more complicated because the media item has it's roots in the media source and is currently not supposed to change. Point 1 seems a valuable addition to the changes we are currently working on to have better notification support.
I can't give you any time commitments for any of these points but I will make sure that 1) is part of the first stable release of Media3.
Hi,
for streaming radio apps it is also a problem, that is not possible to update the MediaSession, respectively the Notification for a MediaItem. Radio streams often publish "Currently Playing" metadata, which can be received in onMetadata() in the Player.Listener. If you want to surface the radio station metadata in the Notification, you need a way to update the Notification / MediaSession.
I think podcast apps that support chapters might run into the same issue.
@y20k The metadata received via onMetadata should already update the notification because it's part of player.getMediaMetadata() (which is used for the notification). If you think this isn't working in specific cases, could you file a new issue for this? This issue here is more updating the app-provided metadata in the MediaItem.
Hi @y20k
I was not aware Icecast metadata is already being put into MediaMetadata. That is great! ❤️ No need to intercept the notification updates when new Icecast metadata arrives.
I think I still found an issue. An important part of Icecast metadata seems not to be used when creating MediaMetadata.
I am not sure, if is a bug, or intended. I created the issue #153 to document my findings.
Not sure I follow on the last few responses here. Is that to say that if a Live audio stream internally holds metadata through this IcyInfo standard it will get automatically picked up?
What about the use case where the stream itself doesn't have such metadata, but information is provided through other means and must be updated in the UI/Notification. For instance an API that returns the currently airing radio program along with a timestamp for when the next program is scheduled to start. Is there no way currently to update the MediaMetadata with information received through this mechanism after the MediaItem containing the stream has started playing? If not that seems like a pretty major shortcoming given that Media3 is geared around automatically setting up behaviours driven by MediaMetadata (e.g. the notification behaviour).
In the app I'm working on I'm also hoping to treat the MediaMetadata as the single source of truth for what to display on the app's own player UI as well.
Looks like the functionality described above (sourcing metadata about a live radio stream) could be achieved through a MediaSource and providing various windows? (Correct me if that is completely the wrong assumption or ballpark).
Though the use of media sources seems to be something that's limited to the ExoPlayer class itself and isn't exposed on the Player interface or MediaController, and playback of MediaItems needs to go through the MediaSession.Callback onAddMediaItems callback to populate the media URI given request metadata. There doesn't seem like an obvious entry point for making use of MediaSources
EDIT: Nevermind, I see the setMediaSourceFactory method on the ExoPlayer builder can be used to generate appropriate media sources given a media item.
This will be possible via replaceMediaItem from 1.2.0.