From 5a3535132e904822ecaf7184b2f46b6d1f630e6f Mon Sep 17 00:00:00 2001
From: Spiros Georgaras
Date: Tue, 24 Nov 2020 14:02:18 +0200
Subject: [PATCH] - Version 0.8.8-beta6 - Fixing playback for m3u8 on vlc -
Fixing "-u" functionality on Windows - Fixing "no player" messages - Stopping
runaway threads from displaying messages after the player is stopped
---
Changelog | 16 +++++++-
pyradio/__init__.py | 2 +-
pyradio/browser.py | 2 -
pyradio/common.py | 6 +++
pyradio/log.py | 22 +++++++++++
pyradio/main.py | 4 +-
pyradio/player.py | 77 +++++++++++++++++++++++---------------
pyradio/radio.py | 91 ++++++++++++++++-----------------------------
windows.html | 4 +-
windows.md | 4 +-
10 files changed, 129 insertions(+), 99 deletions(-)
diff --git a/Changelog b/Changelog
index 9a5d6c27..61645fb4 100644
--- a/Changelog
+++ b/Changelog
@@ -1,5 +1,17 @@
-2020-11-17
- * Versio 0.8.8-beta4
+2020-11-23 s-n-g
+ * Version 0.8.8-beta6
+ * Fixing playback for m3u8 (vlc)
+ * Fixing "-u" functionality on Windows
+ * Fixing "no player" messages
+ * Stopping runaway threads from displaying messages
+ after the player is stopped
+
+2020-11-23 s-n-g
+ * Version 0.8.8-beta5
+ * Adding VLC support on Windows
+
+2020-11-17 s-n-g
+ * Version 0.8.8-beta4
* Adding the "Force http connections" configuration
option (#113)
* Fixed a couple of bugs
diff --git a/pyradio/__init__.py b/pyradio/__init__.py
index dec014e2..c204aee8 100644
--- a/pyradio/__init__.py
+++ b/pyradio/__init__.py
@@ -6,7 +6,7 @@
# New stable version: ''
# Beta version: 'betax', x=1,2,3...
# RC version: 'RCx', x=1,23...
-app_state = 'beta5'
+app_state = 'beta6'
__version__ = version = '.'.join(map(str, version_info))
__project__ = __name__
diff --git a/pyradio/browser.py b/pyradio/browser.py
index a0d5fb45..57bc5727 100644
--- a/pyradio/browser.py
+++ b/pyradio/browser.py
@@ -805,9 +805,7 @@ def get_data_dict(data):
logger.info('Asked to stop after working on "{}"...'.format(an_item))
self._terminated = True
return
- # print('I am here')
lock.acquire()
- # print('I am here too')
callback(my_data, ret)
lock.release()
diff --git a/pyradio/common.py b/pyradio/common.py
index a63cd90e..bc4bfeda 100644
--- a/pyradio/common.py
+++ b/pyradio/common.py
@@ -22,3 +22,9 @@ def BACKGROUND(): return 1
('Normal Cursor', 6, 0, 0),
('Active Cursor', 9, 0, 0),
('Edit Cursor', 8, 0, 0 ) )
+
+""" Messages to display when player starts / stops
+ Used in log to stop runaway threads from printing
+ messages after playback is stopped """
+player_start_stop_token = ('Initialization: "', ': Playback stopped')
+
diff --git a/pyradio/log.py b/pyradio/log.py
index 9f1e6417..296eb9cd 100644
--- a/pyradio/log.py
+++ b/pyradio/log.py
@@ -3,6 +3,7 @@
from sys import version_info
import logging
import threading
+from .common import player_start_stop_token
logger = logging.getLogger(__name__)
@@ -22,6 +23,8 @@ class Log(object):
lock = threading.Lock()
+ _player_stopped = 0
+
def __init__(self):
self.width = None
@@ -45,9 +48,24 @@ def _do_i_print_last_char(self, first_print):
def write(self, msg=None, suffix=None, counter=None, help_msg=False, error_msg=False, notify_function=None):
if self.cursesScreen:
with self.lock:
+ if msg:
+ if player_start_stop_token[1] in msg:
+ self._player_stopped += 1
+ elif msg.startswith(player_start_stop_token[0]):
+ self._player_stopped = 0
+ if msg and self._player_stopped > 1:
+ """ Refuse to print anything if "Playback stopped"
+ was the last message printed
+ """
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug('Refusing to show message; player is stopped: "{}"'.format(msg))
+ #return
+ elif self._player_stopped == 1:
+ self._player_stopped = 2
if self.asked_to_stop:
self.asked_to_stop = False
self.counter = None
+ self._player_stopped = 0
return
#if logger.isEnabledFor(logging.DEBUG):
# logger.debug('before ----------------------------')
@@ -76,6 +94,7 @@ def write(self, msg=None, suffix=None, counter=None, help_msg=False, error_msg=F
if self.asked_to_stop:
self.asked_to_stop = False
self.counter = None
+ self._player_stopped = 0
return
""" update main message """
if self.msg:
@@ -101,6 +120,7 @@ def write(self, msg=None, suffix=None, counter=None, help_msg=False, error_msg=F
if self.asked_to_stop:
self.asked_to_stop = False
self.counter = None
+ self._player_stopped = 0
return
""" display suffix """
if self.suffix:
@@ -153,8 +173,10 @@ def write(self, msg=None, suffix=None, counter=None, help_msg=False, error_msg=F
if self.asked_to_stop:
self.asked_to_stop = False
self.counter = None
+ self._player_stopped = 0
return
self.cursesScreen.refresh()
+ #logger.error('DE _player_stopped = {}'.format(self._player_stopped))
def readline(self):
pass
diff --git a/pyradio/main.py b/pyradio/main.py
index 382ccfec..9ff20900 100644
--- a/pyradio/main.py
+++ b/pyradio/main.py
@@ -141,8 +141,7 @@ def shell():
sys.exit(1)
if args.use_player != '':
- if not platform.startswith('win'):
- requested_player = args.use_player
+ requested_player = args.use_player
if args.list is False and args.add is False:
print('Reading playlist...')
@@ -229,7 +228,6 @@ def print_playlist_selection_error(a_selection, cnf, ret, exit_if_malformed=True
if exit_if_malformed:
if ret == -1:
print('Error: playlist is malformed: "{}"'.format(a_selection))
- #print('Error: playlist is malformed: "{}"'.format(args.stations))
sys.exit(1)
if ret == -2:
diff --git a/pyradio/player.py b/pyradio/player.py
index 87ff9e6d..89c69b98 100644
--- a/pyradio/player.py
+++ b/pyradio/player.py
@@ -444,6 +444,14 @@ def _do_save_volume(self, config_string):
self.PROFILE_FROM_USER = True
return ret_string
+ def _stop_delay_thread(self):
+ if self.delay_thread is not None:
+ try:
+ self.delay_thread.cancel()
+ except:
+ pass
+ self.delay_thread = None
+
def _is_in_playback_token(self, a_string):
for a_token in self._playback_token_tuple:
if a_token in a_string:
@@ -511,6 +519,7 @@ def updateStatus(self, *args):
new_input = self.oldUserInput['Title']
self.outputStream.write(msg=new_input, counter='')
self.playback_is_on = True
+ self._stop_delay_thread()
if 'AO: [' in subsystemOut:
with self.status_update_lock:
if version_info > (3, 0):
@@ -535,7 +544,13 @@ def updateStatus(self, *args):
#logger.error("***** icy_entry")
title = self._format_title_string(subsystemOut)
ok_to_display = False
+ if not self.playback_is_on:
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('*** updateStatus(): Start of playback detected (Icy-Title received) ***')
+ self.playback_is_on = True
if title[len(self.icy_title_prefix):].strip():
+ #self._stop_delay_thread()
+ #logger.error("***** updating title")
self.oldUserInput['Title'] = title
# make sure title will not pop-up while Volume value is on
if self.delay_thread is None:
@@ -543,6 +558,9 @@ def updateStatus(self, *args):
if ok_to_display and self.playback_is_on:
string_to_show = self.title_prefix + title
self.outputStream.write(msg=string_to_show, counter='')
+ else:
+ if logger.isEnabledFor(logging.debug):
+ logger.debug('***** Title change inhibited: ok_to_display = {0}, playbabk_is_on = {1}'.format(ok_to_display, self.playback_is_on))
else:
ok_to_display = True
if (logger.isEnabledFor(logging.INFO)):
@@ -560,10 +578,14 @@ def updateStatus(self, *args):
else:
for a_token in self.icy_audio_tokens.keys():
if a_token in subsystemOut:
- logger.error(' DE token = "{}"'.format(a_token))
- logger.error(' DE icy_audio_tokens[a_token] = "{}"'.format(self.icy_audio_tokens[a_token]))
+ if not self.playback_is_on:
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('*** updateStatus(): Start of playback detected (Icy audio token received) ***')
+ self.playback_is_on = True
+ #logger.error(' DE token = "{}"'.format(a_token))
+ #logger.error(' DE icy_audio_tokens[a_token] = "{}"'.format(self.icy_audio_tokens[a_token]))
a_str = subsystemOut.split(a_token)
- logger.error(' DE str = "{}"'.format(a_str))
+ #logger.error(' DE str = "{}"'.format(a_str))
with self.status_update_lock:
if self.icy_audio_tokens[a_token] == 'icy-br':
self._icy_data[self.icy_audio_tokens[a_token]] = a_str[1].replace('kbit/s', '')
@@ -742,7 +764,7 @@ def updateWinVLCStatus(self, *args):
except:
pass
if (not self.playback_is_on) and (logger.isEnabledFor(logging.INFO)):
- logger.info('*** updateStatus(): Start of playback detected ***')
+ logger.info('*** updateWinVLCStatus(): Start of playback detected ***')
#if self.outputStream.last_written_string.startswith('Connecting to'):
if self.oldUserInput['Title'] == '':
new_input = 'Playing: "{}"'.format(self.name)
@@ -761,6 +783,10 @@ def updateWinVLCStatus(self, *args):
elif self._is_icy_entry(subsystemOut):
if stop():
break
+ if not self.playback_is_on:
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('*** updateWinVLCStatus(): Start of playback detected (Icy-Title received) ***')
+ self.playback_is_on = True
#logger.error("***** icy_entry")
title = self._format_title_string(subsystemOut)
@@ -792,10 +818,14 @@ def updateWinVLCStatus(self, *args):
break
for a_token in self.icy_audio_tokens.keys():
if a_token in subsystemOut:
- logger.error(' DE token = "{}"'.format(a_token))
- logger.error(' DE icy_audio_tokens[a_token] = "{}"'.format(self.icy_audio_tokens[a_token]))
+ if not self.playback_is_on:
+ if logger.isEnabledFor(logging.INFO):
+ logger.info('*** updateWinVLCStatus(): Start of playback detected (Icy audio token received) ***')
+ self.playback_is_on = True
+ #logger.error(' DE token = "{}"'.format(a_token))
+ #logger.error(' DE icy_audio_tokens[a_token] = "{}"'.format(self.icy_audio_tokens[a_token]))
a_str = subsystemOut.split(a_token)
- logger.error(' DE str = "{}"'.format(a_str))
+ #logger.error(' DE str = "{}"'.format(a_str))
with self.status_update_lock:
if self.icy_audio_tokens[a_token] == 'icy-br':
self._icy_data[self.icy_audio_tokens[a_token]] = a_str[1].replace('kbit/s', '')
@@ -988,15 +1018,12 @@ def _set_mpv_playback_is_on(self, stop):
def threadUpdateTitle(self, delay=1):
if self.oldUserInput['Title'] != '':
- try:
- self.delay_thread.cancel()
- except:
- pass
+ self._stop_delay_thread()
try:
self.delay_thread = threading.Timer(delay,
self.updateTitle,
[ self.outputStream,
- self.title_prefix + self._format_title_string(self.oldUserInput['Title']) ]
+ None ]
)
self.delay_thread.start()
except:
@@ -1004,7 +1031,11 @@ def threadUpdateTitle(self, delay=1):
logger.debug("delay thread start failed")
def updateTitle(self, *arg, **karg):
- arg[0].write(msg=arg[1])
+ self._stop_delay_thread()
+ if arg[1]:
+ arg[0].write(msg=arg[1])
+ else:
+ arg[0].write(msg=self.title_prefix + self._format_title_string(self.oldUserInput['Title']))
def _is_icy_entry(self, a_string):
#logger.error("**** a_string = {}".format(a_string))
@@ -1044,6 +1075,7 @@ def play(self, name, streamUrl, encoding = ''):
self.show_volume = True
self.title_prefix = ''
self.playback_is_on = False
+ self.delay_thread = None
self.outputStream.write(msg='Station: "{}" - Opening connection...'.format(name), counter='')
if logger.isEnabledFor(logging.INFO):
logger.info('Selected Station: "{}"'.format(name))
@@ -1130,11 +1162,7 @@ def close(self):
self.connection_timeout_thread.join()
except:
pass
- if self.delay_thread is not None:
- try:
- self.delay_thread.cancel()
- except:
- pass
+ self._stop_delay_thread()
if self.process is not None:
if platform.startswith('win'):
try:
@@ -1165,11 +1193,7 @@ def toggleMute(self):
self.muted = not self.muted
self._mute()
if self.muted:
- if self.delay_thread is not None:
- try:
- self.delay_thread.cancel()
- except:
- pass
+ self._stop_delay_thread()
self.title_prefix = '[Muted] '
self.show_volume = False
else:
@@ -1691,7 +1715,6 @@ class VlcPlayer(Player):
WIN = False
if platform.startswith('win'):
WIN = True
- logger.error('DE WIN = {}'.format(WIN))
PLAYER_CMD = "cvlc"
if WIN:
# TODO: search and finde vlc.exe
@@ -1708,8 +1731,6 @@ class VlcPlayer(Player):
else:
executable_found = False
- print('executable: {}'.format(REAL_PLAYER_CMD))
-
if executable_found:
""" items of this tupple are considered icy-title
and get displayed after first icy-title is received """
@@ -1734,7 +1755,7 @@ class VlcPlayer(Player):
max_volume = 512
""" When found in station transmission, playback is on """
- _playback_token_tuple = ('main audio ', 'Content-Type: audio' )
+ _playback_token_tuple = ('main audio ', 'Content-Type: audio', ' Segment #' )
""" Windows only variables """
_vlc_stdout_log_file = ''
@@ -1951,7 +1972,6 @@ def _req(self, msg, ret_function=None, full=True):
try:
while (True):
received = (sock.recv(4096)).decode()
- #print('received: "{}"'.format(received))
response = response + received
if full:
if response.count("\r\n") > 1:
@@ -1963,7 +1983,6 @@ def _req(self, msg, ret_function=None, full=True):
break
except:
response = response + received
- pass
sock.close()
except:
pass
diff --git a/pyradio/radio.py b/pyradio/radio.py
index 8d3f6541..fa118170 100644
--- a/pyradio/radio.py
+++ b/pyradio/radio.py
@@ -487,61 +487,37 @@ def initBody(self):
self.bodyWin.noutrefresh()
self.outerBodyWin.noutrefresh()
if self.ws.operation_mode == self.ws.NO_PLAYER_ERROR_MODE:
- if platform.startswith('win'):
- txt = '''PyRadio cannot find mplayer.
-
- This means that either mplayer is not installed in this system,
- or its directory is not in the PATH.
-
- Please refer to windows.html help file to fix this problem.
+ if self.requested_player:
+ if self.requested_player in ('mpv', 'mplayer', 'vlc', 'cvlc'):
+ atxt = """PyRadio is not able to use the player you specified.
- To get to this file, execute "pyradio -ocd" and navigate to
- the "help" directory.'''
- else:
- if self.requested_player:
- if self.requested_player in ('mpv', 'mplayer', 'vlc', 'cvlc'):
- atxt = """PyRadio is not able to use the player you specified.
-
- This player ({}) is supported by PyRadio, but it probably
- is not installed in your system.
+ This player ({}) is supported by PyRadio, but it probably
+ is not installed in your system.
- Keep in mind that you can choose a player to use by specifying
- the "-u" command line parameter."""
- txt = atxt.format(self.requested_player)
+ Keep in mind that you can choose a player to use by specifying
+ the "-u" command line parameter."""
+ txt = atxt.format(self.requested_player)
- else:
- txt = """PyRadio is not able to use the player you specified.
-
- This means that either this particular player is not supported
- by PyRadio, or that you have simply misspelled its name.
-
- PyRadio currently supports three players: mpv, mplayer and vlc,
- automatically detected in this order."""
else:
- txt = """PyRadio is not able to detect any players.
+ txt = """PyRadio is not able to use the player you specified.
+
+ This means that either this particular player is not supported
+ by PyRadio, or that you have simply misspelled its name.
PyRadio currently supports three players: mpv, mplayer and vlc,
- automatically detected in this order.
+ automatically detected in this order."""
+ else:
+ txt = """PyRadio is not able to detect any players.
+
+ PyRadio currently supports three players: mpv, mplayer and vlc,
+ automatically detected in this order.
- Please install any one of them and try again."""
+ Please install any one of them and try again."""
+ if platform.startswith('win'):
+ txt = txt.replace('mpv, ', '')
+ txt = txt.replace('three', 'two')
self.refreshNoPlayerBody(txt)
else:
- #if self.ws.operation_mode == self.ws.MAIN_HELP_MODE:
- # self.ws.operation_mode = self.ws.window_mode = self.ws.NORMAL_MODE
- # self.helpWin = None
- # if logger.isEnabledFor(logging.DEBUG):
- # logger.debug('MODE: self.ws.MAIN_HELP_MODE => self.ws.NORMAL_MODE')
- #elif self.ws.operation_mode == self.ws.PLAYLIST_HELP_MODE:
- # self.ws.operation_mode = self.ws.window_mode = self.ws.PLAYLIST_MODE
- # self.helpWin = None
- # if logger.isEnabledFor(logging.DEBUG):
- # logger.debug('MODE: self.ws.PLAYLIST_HELP_MODE => self.ws.PLAYLIST_MODE')
- #elif self.ws.operation_mode == self.ws.THEME_HELP_MODE:
- # self.ws.operation_mode = self.ws.window_mode = self.ws.THEME_MODE
- # self.helpWin = None
- # if logger.isEnabledFor(logging.DEBUG):
- # logger.debug('MODE: self.ws.THEME_HELP_MODE => self.ws.THEME_MODE')
- # make sure selected is visible
self._put_selection_in_the_middle()
self.refreshBody()
@@ -692,16 +668,13 @@ def __displayBodyLine(self, lineNum, pad, station):
def run(self):
if self.ws.operation_mode == self.ws.NO_PLAYER_ERROR_MODE:
- if platform.startswith('win'):
- self.log.write(msg='mplayer not found. Press any key to exit....', error_msg=True)
- else:
- if self.requested_player:
- if ',' in self.requested_player:
- self.log.write(msg='None of "{}" players is available. Press any key to exit....'.format(self.requested_player), error_msg=True)
- else:
- self.log.write(msg='Player "{}" not available. Press any key to exit....'.format(self.requested_player), error_msg=True)
+ if self.requested_player:
+ if ',' in self.requested_player:
+ self.log.write(msg='None of "{}" players is available. Press any key to exit....'.format(self.requested_player), error_msg=True)
else:
- self.log.write(msg="No player available. Press any key to exit....", error_msg=True)
+ self.log.write(msg='Player "{}" not available. Press any key to exit....'.format(self.requested_player), error_msg=True)
+ else:
+ self.log.write(msg="No player available. Press any key to exit....", error_msg=True)
try:
self.bodyWin.getch()
except KeyboardInterrupt:
@@ -855,6 +828,8 @@ def setStation(self, number):
self.startPos = self.selection
def playSelectionBrowser(self):
+ self.log.display_help_message = False
+ self.log.write(msg=player_start_stop_token[0] + self._last_played_station[0] + '"')
#### self._cnf.browsing_station_service = True
# Add a history item to preserve browsing_station_service
# Need to add TITLE, if service found
@@ -872,6 +847,8 @@ def playSelection(self):
self.playing = self.selection
self._last_played_station = self.stations[self.selection]
stream_url = ''
+ self.log.display_help_message = False
+ self.log.write(msg=player_start_stop_token[0] + self._last_played_station[0] + '"')
if self._cnf.browsing_station_service:
if self._cnf.online_browser.have_to_retrieve_url:
self.log.display_help_message = False
@@ -883,8 +860,6 @@ def playSelection(self):
enc = self.stations[self.selection][2].strip()
except:
enc = ''
- self.log.display_help_message = False
- self.log.write(msg='Initialization: "' + self._last_played_station[0] + '"')
try:
self.player.play(self._last_played_station[0], stream_url, self.get_active_encoding(enc))
except OSError:
@@ -960,7 +935,7 @@ def stopPlayer(self, show_message=True):
self._show_player_is_stopped()
def _show_player_is_stopped(self):
- self.log.write(msg='{}: Playback stopped'.format(self._format_player_string()), help_msg=True, suffix=self._status_suffix, counter='')
+ self.log.write(msg='{}'.format(self._format_player_string()) + player_start_stop_token[1], help_msg=True, suffix=self._status_suffix, counter='')
def removeStation(self):
if self._cnf.confirm_station_deletion and not self._cnf.is_register:
diff --git a/windows.html b/windows.html
index 890411ad..fbfefedc 100644
--- a/windows.html
+++ b/windows.html
@@ -74,7 +74,7 @@ How it all works First of all, let me tell you that if you are still running Windows XP, you can just stop reading right now; it won’t happen…
Then, due to reasons that are of no importance right now, mpv is not (yet?) supported. That leaves us with MPlayer and VLC.
Installing MPlayer takes a couple of extra steps, and you may find that some streams (e.g. m3u8) may not be playable. Furthermore, special care has to be taken in order to be able to save the volume of the player.
-VLC is much easier to install, but song titles’ updating may not be 100% consistaent. If this is not a deal breaker for you, then just go on and use VLC as PyRadio’s player.
+VLC is much easier to install, but song titles’ updating may not be 100% consistent (if any). If this is not a deal breaker for you, then just go on and use VLC as PyRadio’s player.
Other than that, you will have a fully functional PyRadio installation.
Having said that, let us proceed with the installation.
Installation Top
@@ -125,7 +125,7 @@ 2. Player installation
Cons |
Extra steps to install May not play all streams (e.g. m3u8) |
-Titles update is not consistent |
+Titles update is not consistent (if any)) |
diff --git a/windows.md b/windows.md
index 05636494..680bfc4b 100644
--- a/windows.md
+++ b/windows.md
@@ -46,7 +46,7 @@ Then, due to reasons that are of no importance right now, [mpv](https://mpv.io/)
Installing [MPlayer](http://www.mplayerhq.hu/) takes a couple of extra steps, and you may find that some streams (e.g. m3u8) may not be playable. Furthermore, special care has to be taken in order to be able to save the volume of the player.
-[VLC](https://www.videolan.org/vlc/) is much easier to install, but song titles' updating may not be 100% consistaent. If this is not a deal breaker for you, then just go on and use [VLC](https://www.videolan.org/vlc/) as **PyRadio**'s player.
+[VLC](https://www.videolan.org/vlc/) is much easier to install, but song titles' updating may not be 100% consistent (if any). If this is not a deal breaker for you, then just go on and use [VLC](https://www.videolan.org/vlc/) as **PyRadio**'s player.
Other than that, you will have a fully functional **PyRadio** installation.
@@ -101,7 +101,7 @@ This is what you should know before making your decision:
| | MPlayer | VLC |
|----------|----------------------------------------------------------------|-----------------------------------------------|
| **Pros** | Fully functional | Easy installation
Plays almost all streams |
-| **Cons** | Extra steps to install
May not play all streams (e.g. m3u8) | Titles update is not consistent |
+| **Cons** | Extra steps to install
May not play all streams (e.g. m3u8) | Titles update is not consistent (if any)) |
#### 2.1 MPlayer installation