1- import { css , html , LitElement , nothing } from 'lit' ;
2- import { property } from 'lit/decorators.js' ;
1+ import { css , html , LitElement , nothing , PropertyValues } from 'lit' ;
2+ import { property , state } from 'lit/decorators.js' ;
33import MediaControlService from '../services/media-control-service' ;
44import Store from '../model/store' ;
55import { CardConfig , PlayerConfig } from '../types' ;
66import {
77 mdiFastForward ,
8+ mdiHeart ,
9+ mdiHeartOutline ,
810 mdiPauseCircle ,
911 mdiPlayBoxMultiple ,
1012 mdiPlayCircle ,
@@ -21,27 +23,45 @@ import MediaBrowseService from '../services/media-browse-service';
2123
2224class PlayerControls extends LitElement {
2325 @property ( { attribute : false } ) store ! : Store ;
26+ @state ( ) private isFavorite : boolean | null = null ;
27+ @state ( ) private favoriteLoading = false ;
2428 private config ! : CardConfig ;
2529 private playerConfig ! : PlayerConfig ;
2630 private activePlayer ! : MediaPlayer ;
2731 private mediaControlService ! : MediaControlService ;
2832 private mediaBrowseService ! : MediaBrowseService ;
2933 private volumePlayer ! : MediaPlayer ;
3034 private updateMemberVolumes ! : boolean ;
35+ private lastMediaContentId : string | undefined ;
36+
37+ protected willUpdate ( changedProperties : PropertyValues ) : void {
38+ if ( changedProperties . has ( 'store' ) ) {
39+ this . config = this . store . config ;
40+ this . playerConfig = this . config . player ?? { } ;
41+ this . activePlayer = this . store . activePlayer ;
42+ this . mediaControlService = this . store . mediaControlService ;
43+ this . mediaBrowseService = this . store . mediaBrowseService ;
44+
45+ const isMusicAssistant = this . store . hassService . musicAssistantService . isMusicAssistantPlayer ( this . activePlayer ) ;
46+ const currentMediaContentId = this . activePlayer . attributes . media_content_id ;
47+ if ( isMusicAssistant && currentMediaContentId !== this . lastMediaContentId ) {
48+ this . lastMediaContentId = currentMediaContentId ;
49+ this . isFavorite = null ;
50+ this . favoriteLoading = false ;
51+ this . refreshFavoriteStatus ( ) ;
52+ }
53+ }
54+ }
3155
3256 render ( ) {
33- this . config = this . store . config ;
34- this . playerConfig = this . config . player ?? { } ;
35- this . activePlayer = this . store . activePlayer ;
36- this . mediaControlService = this . store . mediaControlService ;
37- this . mediaBrowseService = this . store . mediaBrowseService ;
3857 const noUpDown = ! ! this . playerConfig . showVolumeUpAndDownButtons && nothing ;
3958 const noFastForwardAndRewind = ! ! this . playerConfig . showFastForwardAndRewindButtons && nothing ;
4059 const noShuffle = ! this . playerConfig . hideControlShuffleButton && nothing ;
4160 const noPrev = ! this . playerConfig . hideControlPrevTrackButton && nothing ;
4261 const noNext = ! this . playerConfig . hideControlNextTrackButton && nothing ;
4362 const noRepeat = ! this . playerConfig . hideControlRepeatButton && nothing ;
4463 const noBrowse = ! ! this . playerConfig . showBrowseMediaButton && nothing ;
64+ const isMusicAssistant = this . store . hassService . musicAssistantService . isMusicAssistantPlayer ( this . activePlayer ) ;
4565
4666 this . volumePlayer = this . getVolumePlayer ( ) ;
4767 this . updateMemberVolumes = ! this . playerConfig . volumeEntityId ;
@@ -55,7 +75,21 @@ class PlayerControls extends LitElement {
5575 </ style>
5676 <div class= "main" id = "mediaControls" >
5777 <div class= "icons ${ this . playerConfig . controlsLargeIcons ? 'large-icons' : '' } " >
58- <div class= "flex-1" > </ div>
78+ <div class= "flex-1" >
79+ ${
80+ isMusicAssistant
81+ ? html `<ha- icon- butto n
82+ class= "favorite-button ${ this . isFavorite ? 'is-favorite' : '' } ${ this . favoriteLoading
83+ ? 'loading'
84+ : '' } "
85+ @click = ${ this . toggleFavorite }
86+ .path = ${ this . isFavorite ? mdiHeart : mdiHeartOutline }
87+ title= ${ this . isFavorite ? 'Remove from favorites' : 'Add to favorites' }
88+ ?dis abled= ${ this . favoriteLoading }
89+ > </ ha- icon- butto n> `
90+ : nothing
91+ }
92+ </ div>
5993 <ha- icon- butto n hide= ${ noUpDown } @click = ${ this . volDown } .path = ${ mdiVolumeMinus } > </ ha- icon- butto n>
6094 <sonos- shuffle hide= ${ noShuffle } .store = ${ this . store } > </ sonos- shuffle>
6195 <ha- icon- butto n hide= ${ noPrev } @click = ${ this . prev } .path = ${ mdiSkipPrevious } > </ ha- icon- butto n>
@@ -119,6 +153,40 @@ class PlayerControls extends LitElement {
119153 this . activePlayer . attributes . media_position + ( this . playerConfig . fastForwardAndRewindStepSizeSeconds || 15 ) ,
120154 ) ;
121155
156+ private async refreshFavoriteStatus ( ) {
157+ const songIdAtStart = this . activePlayer . attributes . media_content_id ;
158+ const favorite = await this . store . hassService . musicAssistantService . getCurrentSongFavorite ( this . activePlayer ) ;
159+ // Only update if song hasn't changed during the async call
160+ if ( this . activePlayer . attributes . media_content_id === songIdAtStart ) {
161+ this . isFavorite = favorite ;
162+ }
163+ }
164+
165+ private toggleFavorite = async ( ) => {
166+ if ( this . favoriteLoading ) {
167+ return ;
168+ }
169+ const songIdAtStart = this . activePlayer . attributes . media_content_id ;
170+ this . favoriteLoading = true ;
171+ try {
172+ if ( this . isFavorite ) {
173+ const success = await this . store . hassService . musicAssistantService . unfavoriteCurrentSong ( this . activePlayer ) ;
174+ // Only update UI if song hasn't changed
175+ if ( success && this . activePlayer . attributes . media_content_id === songIdAtStart ) {
176+ this . isFavorite = false ;
177+ }
178+ } else {
179+ const success = await this . store . hassService . musicAssistantService . favoriteCurrentSong ( this . activePlayer ) ;
180+ // Only update UI if song hasn't changed
181+ if ( success && this . activePlayer . attributes . media_content_id === songIdAtStart ) {
182+ this . isFavorite = true ;
183+ }
184+ }
185+ } finally {
186+ this . favoriteLoading = false ;
187+ }
188+ } ;
189+
122190 static get styles ( ) {
123191 return css `
124192 .main {
@@ -152,6 +220,12 @@ class PlayerControls extends LitElement {
152220 .browse-button {
153221 float: right;
154222 }
223+ .favorite-button.is-favorite {
224+ color: var(--accent-color);
225+ }
226+ .favorite-button.loading {
227+ opacity: 0.5;
228+ }
155229
156230 .large-icons {
157231 margin-bottom: 2rem;
0 commit comments