@@ -48,6 +48,9 @@ class Player(object):
48
48
49
49
_station_encoding = 'utf-8'
50
50
51
+ # used to stop mpv update thread on python3
52
+ stop_mpv_status_update_thread = False
53
+
51
54
def __init__ (self , outputStream , playback_timeout , playback_timeout_handler ):
52
55
self .outputStream = outputStream
53
56
try :
@@ -222,7 +225,7 @@ def updateStatus(self, *args):
222
225
if self .volume_string in subsystemOut :
223
226
# disable volume for mpv
224
227
if self .PLAYER_CMD != "mpv" :
225
- logger .error ("***** volume" )
228
+ # logger.error("***** volume")
226
229
if self .oldUserInput ['Volume' ] != subsystemOut :
227
230
self .oldUserInput ['Volume' ] = subsystemOut
228
231
self .volume = '' .join (c for c in subsystemOut if c .isdigit ())
@@ -268,8 +271,6 @@ def updateStatus(self, *args):
268
271
else :
269
272
if self .oldUserInput ['Title' ] == '' :
270
273
self .oldUserInput ['Title' ] = 'Connecting to: "{}"' .format (self .name )
271
- if (logger .isEnabledFor (logging .INFO )):
272
- logger .info ('Connecting to: "{}"' .format (self .name ))
273
274
self .outputStream .write (self .oldUserInput ['Title' ])
274
275
except :
275
276
has_error = True
@@ -279,6 +280,89 @@ def updateStatus(self, *args):
279
280
if (logger .isEnabledFor (logging .INFO )):
280
281
logger .info ("updateStatus thread stopped." )
281
282
283
+ def updateMPVStatus (self , * args ):
284
+ if (logger .isEnabledFor (logging .INFO )):
285
+ logger .info ("MPV updateStatus thread started." )
286
+
287
+ while True :
288
+ try :
289
+ sock = self ._connect_to_socket (self .mpvsocket )
290
+ finally :
291
+ if sock :
292
+ break
293
+ if args [1 ]():
294
+ if (logger .isEnabledFor (logging .INFO )):
295
+ logger .info ("MPV updateStatus thread stopped (no connection to socket)." )
296
+ return
297
+ sleep (.25 )
298
+ #if (logger.isEnabledFor(logging.INFO)):
299
+ # logger.info("MPV updateStatus thread connected to socket.")
300
+ self .oldUserInput ['Title' ] = 'Connecting to: "{}"' .format (self .name )
301
+ self .outputStream .write (self .oldUserInput ['Title' ], args [0 ])
302
+ # Send data
303
+ message = b'{ "command": ["observe_property", 1, "filtered_metadata"] }\n '
304
+ sock .sendall (message )
305
+
306
+ GET_TITLE = b'{ "command": ["get_property", "filtered-metadata"] }\n '
307
+
308
+ while True :
309
+ if args [1 ]():
310
+ break
311
+ try :
312
+ data = sock .recvmsg (4096 )
313
+ if isinstance (data , tuple ):
314
+ a_data = data [0 ]
315
+ else :
316
+ a_data = data
317
+ #logger.error('DE Received: "{!r}"'.format(a_data))
318
+
319
+ if a_data == b'' or args [1 ]():
320
+ break
321
+
322
+ if a_data :
323
+ if b'"icy-title":"' in a_data :
324
+ title = a_data .split (b'"icy-title":"' )[1 ].split (b'"}' )[0 ]
325
+ if title :
326
+ try :
327
+ self .oldUserInput ['Title' ] = 'Title: ' + title .decode (self ._station_encoding , "replace" )
328
+ except :
329
+ self .oldUserInput ['Title' ] = 'Title: ' + title .decode ("utf-8" , "replace" )
330
+ string_to_show = self .title_prefix + self .oldUserInput ['Title' ]
331
+ if args [1 ]():
332
+ break
333
+ self .outputStream .write (string_to_show , args [0 ])
334
+ else :
335
+ if (logger .isEnabledFor (logging .INFO )):
336
+ logger .info ('Icy-Title is NOT valid' )
337
+ else :
338
+ all_data = a_data .split (b'\n ' )
339
+ for n in all_data :
340
+ try :
341
+ d = json .loads (n )
342
+ if 'event' in d .keys ():
343
+ if d ['event' ] == 'metadata-update' :
344
+ sock .sendall (GET_TITLE )
345
+ elif d ['event' ] == 'playback-restart' :
346
+ if self .connection_timeout_thread is not None :
347
+ self .connection_timeout_thread .cancel ()
348
+ if (logger .isEnabledFor (logging .INFO )):
349
+ logger .info ('start of playback detected' )
350
+ if self .outputStream .last_written_string .startswith ('Connecting ' ):
351
+ new_input = self .outputStream .last_written_string .replace ('Connecting to' , 'Playing' )
352
+ self .outputStream .write (new_input , args [0 ])
353
+ if self .oldUserInput ['Title' ] == '' :
354
+ self .oldUserInput ['Input' ] = new_input
355
+ else :
356
+ self .oldUserInput ['Title' ] = new_input
357
+ self .playback_is_on = True
358
+ except :
359
+ pass
360
+ finally :
361
+ pass
362
+ sock .close ()
363
+ if (logger .isEnabledFor (logging .INFO )):
364
+ logger .info ("MPV updateStatus thread stopped." )
365
+
282
366
def threadUpdateTitle (self , a_lock , delay = 1 ):
283
367
if self .oldUserInput ['Title' ] != '' :
284
368
if self .delay_thread is not None :
@@ -342,11 +426,19 @@ def play(self, name, streamUrl, encoding = ''):
342
426
opts = []
343
427
isPlayList = streamUrl .split ("?" )[0 ][- 3 :] in ['m3u' , 'pls' ]
344
428
opts = self ._buildStartOpts (streamUrl , isPlayList )
345
- self .process = subprocess .Popen (opts , shell = False ,
346
- stdout = subprocess .PIPE ,
347
- stdin = subprocess .PIPE ,
348
- stderr = subprocess .STDOUT )
349
- t = threading .Thread (target = self .updateStatus , args = (self .status_update_lock , ))
429
+ self .stop_mpv_status_update_thread = False
430
+ if self .PLAYER_CMD == "mpv" and version_info > (3 , 0 ):
431
+ self .process = subprocess .Popen (opts , shell = False ,
432
+ stdout = subprocess .DEVNULL ,
433
+ stdin = subprocess .DEVNULL ,
434
+ stderr = subprocess .DEVNULL )
435
+ t = threading .Thread (target = self .updateMPVStatus , args = (self .status_update_lock , lambda : self .stop_mpv_status_update_thread ))
436
+ else :
437
+ self .process = subprocess .Popen (opts , shell = False ,
438
+ stdout = subprocess .PIPE ,
439
+ stdin = subprocess .PIPE ,
440
+ stderr = subprocess .STDOUT )
441
+ t = threading .Thread (target = self .updateStatus , args = (self .status_update_lock , ))
350
442
t .start ()
351
443
# start playback check timer thread
352
444
try :
@@ -409,17 +501,18 @@ def _buildStartOpts(self, streamUrl, playList):
409
501
def toggleMute (self ):
410
502
""" mute / unmute player """
411
503
412
- if not self .muted :
504
+ if self .PLAYER_CMD == 'mpv' :
505
+ self .muted = self ._mute ()
506
+ else :
507
+ self .muted = not self .muted
413
508
self ._mute ()
509
+ if self .muted :
414
510
if self .delay_thread is not None :
415
511
self .delay_thread .cancel ()
416
512
self .title_prefix = '[Muted] '
417
- self .muted = True
418
513
self .show_volume = False
419
514
else :
420
- self ._mute ()
421
515
self .title_prefix = ''
422
- self .muted = False
423
516
self .show_volume = True
424
517
if self .oldUserInput ['Title' ] == '' :
425
518
self .outputStream .write (self .title_prefix + self ._format_title_string (self .oldUserInput ['Input' ]))
@@ -579,13 +672,46 @@ def _mute(self):
579
672
ret = self ._send_mpv_command ('mute' )
580
673
while not ret :
581
674
ret = self ._send_mpv_command ('mute' )
675
+ return self ._get_mute_status ()
676
+
677
+ def _get_mute_status (self ):
678
+ got_it = True
679
+ while True :
680
+ sock = self ._connect_to_socket (self .mpvsocket )
681
+ sock .sendall (b'{ "command": ["get_property", "mute"] }\n ' )
682
+ # wait for response
683
+ try :
684
+ if version_info < (3 , 0 ):
685
+ data = sock .recv (4096 )
686
+ else :
687
+ data = sock .recvmsg (4096 )
688
+ if isinstance (data , tuple ):
689
+ a_data = data [0 ]
690
+ else :
691
+ a_data = data
692
+ #logger.error('DE Received: "{!r}"'.format(a_data))
693
+
694
+ if a_data :
695
+ all_data = a_data .split (b'\n ' )
696
+ for n in all_data :
697
+ try :
698
+ d = json .loads (n )
699
+ if d ['error' ] == 'success' :
700
+ if isinstance (d ['data' ], bool ):
701
+ sock .close ()
702
+ return d ['data' ]
703
+ except :
704
+ pass
705
+ finally :
706
+ pass
582
707
583
708
def pause (self ):
584
709
""" pause streaming (if possible) """
585
710
self ._send_mpv_command ('pause' )
586
711
587
712
def _stop (self ):
588
713
""" exit pyradio (and kill mpv instance) """
714
+ self .stop_mpv_status_update_thread = True
589
715
self ._send_mpv_command ('quit' )
590
716
os .system ("rm " + self .mpvsocket + " 2>/dev/null" );
591
717
0 commit comments