Skip to content

Commit a9b808f

Browse files
committed
mpv on python3 uses socket only (no stdout parsing)
1 parent fa9ab98 commit a9b808f

File tree

4 files changed

+146
-15
lines changed

4 files changed

+146
-15
lines changed

Changelog

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
2019-12-22 s-n-g
1+
2019-12-23 s-n-g
2+
* Version 0.8.7
23
* Fixing volume issue with mpv
4+
* mpv on python3 uses socket communication only; no stdout parsing
5+
done anymore, as it is still done on python2, due to title
6+
(icy-title) encoding problems.
37
* socat is no longer needed to use mpv
48

59
2019-12-14 s-n-g

pyradio/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
" pyradio -- Console radio player. "
22

3-
version_info = (0, 8, 6)
3+
version_info = (0, 8, 7)
44

55
__version__ = version = '.'.join(map(str, version_info))
66
__project__ = __name__

pyradio/player.py

+138-12
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class Player(object):
4848

4949
_station_encoding = 'utf-8'
5050

51+
# used to stop mpv update thread on python3
52+
stop_mpv_status_update_thread = False
53+
5154
def __init__(self, outputStream, playback_timeout, playback_timeout_handler):
5255
self.outputStream = outputStream
5356
try:
@@ -222,7 +225,7 @@ def updateStatus(self, *args):
222225
if self.volume_string in subsystemOut:
223226
# disable volume for mpv
224227
if self.PLAYER_CMD != "mpv":
225-
logger.error("***** volume")
228+
#logger.error("***** volume")
226229
if self.oldUserInput['Volume'] != subsystemOut:
227230
self.oldUserInput['Volume'] = subsystemOut
228231
self.volume = ''.join(c for c in subsystemOut if c.isdigit())
@@ -268,8 +271,6 @@ def updateStatus(self, *args):
268271
else:
269272
if self.oldUserInput['Title'] == '':
270273
self.oldUserInput['Title'] = 'Connecting to: "{}"'.format(self.name)
271-
if (logger.isEnabledFor(logging.INFO)):
272-
logger.info('Connecting to: "{}"'.format(self.name))
273274
self.outputStream.write(self.oldUserInput['Title'])
274275
except:
275276
has_error = True
@@ -279,6 +280,89 @@ def updateStatus(self, *args):
279280
if (logger.isEnabledFor(logging.INFO)):
280281
logger.info("updateStatus thread stopped.")
281282

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+
282366
def threadUpdateTitle(self, a_lock, delay=1):
283367
if self.oldUserInput['Title'] != '':
284368
if self.delay_thread is not None:
@@ -342,11 +426,19 @@ def play(self, name, streamUrl, encoding = ''):
342426
opts = []
343427
isPlayList = streamUrl.split("?")[0][-3:] in ['m3u', 'pls']
344428
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, ))
350442
t.start()
351443
# start playback check timer thread
352444
try:
@@ -409,17 +501,18 @@ def _buildStartOpts(self, streamUrl, playList):
409501
def toggleMute(self):
410502
""" mute / unmute player """
411503

412-
if not self.muted:
504+
if self.PLAYER_CMD == 'mpv':
505+
self.muted = self._mute()
506+
else:
507+
self.muted = not self.muted
413508
self._mute()
509+
if self.muted:
414510
if self.delay_thread is not None:
415511
self.delay_thread.cancel()
416512
self.title_prefix = '[Muted] '
417-
self.muted = True
418513
self.show_volume = False
419514
else:
420-
self._mute()
421515
self.title_prefix = ''
422-
self.muted = False
423516
self.show_volume = True
424517
if self.oldUserInput['Title'] == '':
425518
self.outputStream.write(self.title_prefix + self._format_title_string(self.oldUserInput['Input']))
@@ -579,13 +672,46 @@ def _mute(self):
579672
ret = self._send_mpv_command('mute')
580673
while not ret:
581674
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
582707

583708
def pause(self):
584709
""" pause streaming (if possible) """
585710
self._send_mpv_command('pause')
586711

587712
def _stop(self):
588713
""" exit pyradio (and kill mpv instance) """
714+
self.stop_mpv_status_update_thread = True
589715
self._send_mpv_command('quit')
590716
os.system("rm " + self.mpvsocket + " 2>/dev/null");
591717

pyradio/radio.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,8 @@ def connectionFailed(self):
820820
#self._redisplay_transient_window()
821821
self.refreshBody(start=1)
822822
if logger.isEnabledFor(logging.INFO):
823-
logger.info('start of playback NOT detected for: "{}"'.format(self._last_played_station))
823+
logger.info('start of playback NOT detected!!!')
824+
self.player.stop_mpv_status_update_thread = True
824825
self.log.write('Failed to connect to: "{}"'.format(self._last_played_station))
825826
if self._random_requested and \
826827
self.ws.operation_mode == self.ws.NORMAL_MODE:

0 commit comments

Comments
 (0)