100
100
from jukebox .NvManager import nv_manager
101
101
from .playcontentcallback import PlayContentCallbacks , PlayCardState
102
102
from .coverart_cache_manager import CoverartCacheManager
103
+ from .play_position_tracker import PlayPositionTracker
103
104
104
105
logger = logging .getLogger ('jb.PlayerMPD' )
105
106
cfg = jukebox .cfghandler .get_handler ('jukebox' )
@@ -193,6 +194,9 @@ def __init__(self):
193
194
# Change this to last_played_folder and shutdown_state (for restoring)
194
195
self .music_player_status ['player_status' ]['last_played_folder' ] = ''
195
196
197
+ play_position_tracker_file = cfg .getn ('playermpd' , 'play_position_tracker_file' , default = '../../shared/settings/play_positions.json' )
198
+ self .play_position_tracker = PlayPositionTracker (path = play_position_tracker_file )
199
+
196
200
self .old_song = None
197
201
self .mpd_status = {}
198
202
self .mpd_status_poll_interval = 0.25
@@ -270,6 +274,7 @@ def _mpd_status_poll(self):
270
274
self .current_folder_status ["LOOP" ] = "OFF"
271
275
self .current_folder_status ["SINGLE" ] = "OFF"
272
276
277
+ self .play_position_tracker .handle_mpd_status (self .mpd_status )
273
278
# Delete the volume key to avoid confusion
274
279
# Volume is published via the 'volume' component!
275
280
try :
@@ -308,11 +313,13 @@ def update_wait(self):
308
313
def play (self ):
309
314
with self .mpd_lock :
310
315
self .mpd_client .play ()
316
+ self .play_position_tracker .flush ()
311
317
312
318
@plugs .tag
313
319
def stop (self ):
314
320
with self .mpd_lock :
315
321
self .mpd_client .stop ()
322
+ self .play_position_tracker .flush ()
316
323
317
324
@plugs .tag
318
325
def pause (self , state : int = 1 ):
@@ -323,24 +330,28 @@ def pause(self, state: int = 1):
323
330
"""
324
331
with self .mpd_lock :
325
332
self .mpd_client .pause (state )
333
+ self .play_position_tracker .flush ()
326
334
327
335
@plugs .tag
328
336
def prev (self ):
329
337
logger .debug ("Prev" )
330
338
with self .mpd_lock :
331
339
self .mpd_client .previous ()
340
+ self .play_position_tracker .flush ()
332
341
333
342
@plugs .tag
334
343
def next (self ):
335
344
"""Play next track in current playlist"""
336
345
logger .debug ("Next" )
337
346
with self .mpd_lock :
338
347
self .mpd_client .next ()
348
+ self .play_position_tracker .flush ()
339
349
340
350
@plugs .tag
341
351
def seek (self , new_time ):
342
352
with self .mpd_lock :
343
353
self .mpd_client .seekcur (new_time )
354
+ self .play_position_tracker .flush ()
344
355
345
356
@plugs .tag
346
357
def rewind (self ):
@@ -351,6 +362,7 @@ def rewind(self):
351
362
logger .debug ("Rewind" )
352
363
with self .mpd_lock :
353
364
self .mpd_client .play (1 )
365
+ self .play_position_tracker .flush ()
354
366
355
367
@plugs .tag
356
368
def replay (self ):
@@ -367,6 +379,7 @@ def toggle(self):
367
379
"""Toggle pause state, i.e. do a pause / resume depending on current state"""
368
380
with self .mpd_lock :
369
381
self .mpd_client .pause ()
382
+ self .play_position_tracker .flush ()
370
383
371
384
@plugs .tag
372
385
def replay_if_stopped (self ):
@@ -466,11 +479,33 @@ def move(self):
466
479
467
480
@plugs .tag
468
481
def play_single (self , song_url ):
482
+ play_target = ('single' , song_url )
469
483
with self .mpd_lock :
484
+ if self ._play_or_pause_current (play_target ):
485
+ return
470
486
self .mpd_client .clear ()
471
487
self .mpd_client .addid (song_url )
488
+ self ._mpd_restore_saved_position (play_target )
472
489
self .mpd_client .play ()
473
490
491
+ def _play_or_pause_current (self , play_target ):
492
+ if self .play_position_tracker .is_current_play_target (play_target ):
493
+ if self .mpd_status ['state' ] == 'play' :
494
+ # Do nothing
495
+ return True
496
+ if self .mpd_status ['state' ] == 'pause' :
497
+ logger .debug ('Unpausing as the play target is identical' )
498
+ self .mpd_client .play ()
499
+ return True
500
+ return False
501
+
502
+ def _mpd_restore_saved_position (self , play_target ):
503
+ logger .debug (f'Restoring saved position for { play_target } ' )
504
+ playlist_position = self .play_position_tracker .get_playlist_position_by_play_target (play_target ) or 0
505
+ seek_position = self .play_position_tracker .get_seek_position_by_play_target (play_target ) or 0
506
+ self .play_position_tracker .set_current_play_target (play_target )
507
+ self .mpd_client .seek (playlist_position , seek_position )
508
+
474
509
@plugs .tag
475
510
def resume (self ):
476
511
with self .mpd_lock :
@@ -482,11 +517,14 @@ def resume(self):
482
517
@plugs .tag
483
518
def play_card (self , folder : str , recursive : bool = False ):
484
519
"""
485
- Main entry point for trigger music playing from RFID reader. Decodes second swipe options before playing folder content
520
+ Deprecated (?) main entry point for trigger music playing from RFID reader.
521
+ Decodes second swipe options before playing folder content
486
522
487
523
Checks for second (or multiple) trigger of the same folder and calls first swipe / second swipe action
488
524
accordingly.
489
525
526
+ Note: The Web UI currently uses play_single/album/folder directly.
527
+
490
528
:param folder: Folder path relative to music library path
491
529
:param recursive: Add folder recursively
492
530
"""
@@ -587,8 +625,11 @@ def play_folder(self, folder: str, recursive: bool = False) -> None:
587
625
:param recursive: Add folder recursively
588
626
"""
589
627
# TODO: This changes the current state -> Need to save last state
628
+ play_target = ('folder' , folder , recursive )
590
629
with self .mpd_lock :
591
630
logger .info (f"Play folder: '{ folder } '" )
631
+ if self ._play_or_pause_current (play_target ):
632
+ return
592
633
self .mpd_client .clear ()
593
634
594
635
plc = playlistgenerator .PlaylistCollector (components .player .get_music_library_path ())
@@ -608,6 +649,7 @@ def play_folder(self, folder: str, recursive: bool = False) -> None:
608
649
if self .current_folder_status is None :
609
650
self .current_folder_status = self .music_player_status ['audio_folder_status' ][folder ] = {}
610
651
652
+ self ._mpd_restore_saved_position (play_target )
611
653
self .mpd_client .play ()
612
654
613
655
@plugs .tag
@@ -621,10 +663,14 @@ def play_album(self, albumartist: str, album: str):
621
663
:param albumartist: Artist of the Album provided by MPD database
622
664
:param album: Album name provided by MPD database
623
665
"""
666
+ play_target = ('album' , albumartist , album )
624
667
with self .mpd_lock :
625
668
logger .info (f"Play album: '{ album } ' by '{ albumartist } " )
669
+ if self ._play_or_pause_current (play_target ):
670
+ return
626
671
self .mpd_client .clear ()
627
672
self .mpd_retry_with_mutex (self .mpd_client .findadd , 'albumartist' , albumartist , 'album' , album )
673
+ self ._mpd_restore_saved_position (play_target )
628
674
self .mpd_client .play ()
629
675
630
676
@plugs .tag
0 commit comments