Skip to content

Commit 130ccad

Browse files
committed
- Version 0.8.8.2
- Gracefully exit when the terminal is closed
1 parent 1b20bf8 commit 130ccad

File tree

5 files changed

+144
-60
lines changed

5 files changed

+144
-60
lines changed

Changelog

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2020-12-18 s-n-g
2+
* Version 0.8.8.2
3+
* Gracefully exit when the terminal is closed
4+
5+
2020-12-14 s-n-g
6+
* Version 0.8.8.1
7+
* Fixing (?) vlc terminataion on Windows
8+
* Restarting radio-browser.info implementation
9+
110
2020-12-10 s-n-g
211
* Starting 0.8.8
312
* Implementing "Paste to playlist" (\p) command

pyradio/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
" pyradio -- Console radio player. "
22

3-
version_info = (0, 8, 8, 1)
3+
version_info = (0, 8, 8, 2)
44

55
# Application state:
66
# New stable version: ''

pyradio/main.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -99,25 +99,6 @@ def shell():
9999
pyradio_config.force_to_remove_lock_file = True
100100
sys.exit()
101101

102-
# set window title
103-
if platform.startswith('win'):
104-
import ctypes
105-
try:
106-
if pyradio_config.locked:
107-
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player (Session Locked)")
108-
else:
109-
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player")
110-
except:
111-
pass
112-
else:
113-
try:
114-
if pyradio_config.locked:
115-
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player (Session Locked)\x07")
116-
else:
117-
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player\x07")
118-
except:
119-
pass
120-
121102
if args.show_config_dir:
122103
print('PyRadio config dir: "{}"'.format(pyradio_config.stations_dir))
123104
sys.exit()
@@ -211,13 +192,16 @@ def shell():
211192
theme_to_use = pyradio_config.theme
212193

213194
# Starts the radio TUI.
214-
pyradio = PyRadio(pyradio_config,
215-
play=args.play,
216-
req_player=requested_player,
217-
theme=theme_to_use)
195+
pyradio = PyRadio(
196+
pyradio_config,
197+
play=args.play,
198+
req_player=requested_player,
199+
theme=theme_to_use
200+
)
218201
""" Setting ESCAPE key delay to 25ms
219202
Refer to: https://stackoverflow.com/questions/27372068/why-does-the-escape-key-have-a-delay-in-python-curses"""
220203
environ.setdefault('ESCDELAY', '25')
204+
set_terminal_title()
221205
curses.wrapper(pyradio.setup)
222206
if pyradio.setup_return_status:
223207
print('\nThank you for using PyRadio. Cheers!')
@@ -267,6 +251,26 @@ def print_playlist_selection_error(a_selection, cnf, ret, exit_if_malformed=True
267251
print('File type not supported')
268252
sys.exit(1)
269253

254+
def set_terminal_title():
255+
# set window title
256+
if platform.startswith('win'):
257+
import ctypes
258+
try:
259+
if pyradio_config.locked:
260+
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player (Session Locked)")
261+
else:
262+
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player")
263+
except:
264+
pass
265+
else:
266+
try:
267+
if pyradio_config.locked:
268+
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player (Session Locked)\x07")
269+
else:
270+
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player\x07")
271+
except:
272+
pass
273+
270274
def open_conf_dir(cnf):
271275
import subprocess
272276
import os

pyradio/player.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,8 +1155,14 @@ def _sendCommand(self, command):
11551155
if logger.isEnabledFor(logging.ERROR):
11561156
logger.error(msg.format(command).strip(), exc_info=True)
11571157

1158+
def close_from_windows(self):
1159+
""" kill player instance when window console is closed """
1160+
if self.process:
1161+
self.close()
1162+
self._stop()
1163+
11581164
def close(self):
1159-
""" exit pyradio (and kill player instance) """
1165+
""" kill player instance """
11601166

11611167
self._no_mute_on_stop_playback()
11621168

@@ -1413,7 +1419,7 @@ def pause(self):
14131419
self._send_mpv_command('pause')
14141420

14151421
def _stop(self):
1416-
""" exit pyradio (and kill mpv instance) """
1422+
""" kill mpv instance """
14171423
self.stop_mpv_status_update_thread = True
14181424
self._send_mpv_command('quit')
14191425
os.system("rm " + self.mpvsocket + " 2>/dev/null");
@@ -1687,7 +1693,7 @@ def pause(self):
16871693
self._sendCommand("p")
16881694

16891695
def _stop(self):
1690-
""" exit pyradio (and kill mplayer instance) """
1696+
""" kill mplayer instance """
16911697
self._sendCommand("q")
16921698
self._icy_data = {}
16931699

@@ -1844,7 +1850,7 @@ def pause(self):
18441850
self._sendCommand("stop\n")
18451851

18461852
def _stop(self):
1847-
""" exit pyradio (and kill vlc instance) """
1853+
""" kill vlc instance """
18481854
logger.error('setting self.stop_win_vlc_status_update_thread = True')
18491855
self.stop_win_vlc_status_update_thread = True
18501856
if self.ctrl_c_pressed:
@@ -1853,7 +1859,7 @@ def _stop(self):
18531859
if self.process:
18541860
logger.error('>>>> Terminating process')
18551861
self._req('quit')
1856-
threading.Thread(target=self._remove_vlc_stdout_log_file, args=()).start()
1862+
threading.Thread(target=self._remove_vlc_stdout_log_file, args=()).start()
18571863
else:
18581864
self._sendCommand("shutdown\n")
18591865
self._icy_data = {}

pyradio/radio.py

Lines changed: 96 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import logging
1414
import os
1515
import random
16-
#import signal
16+
import signal
1717
from sys import version as python_version, version_info, platform
1818
from os.path import join, basename, getmtime, getsize
1919
from os import remove
@@ -183,6 +183,7 @@ def ll(self, msg):
183183
logger.error('DE p REGISTER_MODE: {0}, {1}, {2}'.format(*self.playlist_selections[2]))
184184

185185
def __init__(self, pyradio_config, play=False, req_player='', theme=''):
186+
self._system_asked_to_terminate = False
186187
self._cnf = pyradio_config
187188
self._theme = PyRadioTheme(self._cnf)
188189
if theme:
@@ -694,6 +695,7 @@ def __displayBodyLine(self, lineNum, pad, station):
694695
self.bodyWin.chgat(lineNum, n, 1, sep_col)
695696

696697
def run(self):
698+
self._register_signals_handlers()
697699
if self.ws.operation_mode == self.ws.NO_PLAYER_ERROR_MODE:
698700
if self.requested_player:
699701
if ',' in self.requested_player:
@@ -707,7 +709,6 @@ def run(self):
707709
except KeyboardInterrupt:
708710
pass
709711
else:
710-
self._register_windows_handlers()
711712

712713
# start update detection and notification thread
713714
if CAN_CHECK_FOR_UPDATES:
@@ -751,7 +752,7 @@ def run(self):
751752
return
752753
except KeyboardInterrupt:
753754
if logger.isEnabledFor(logging.DEBUG):
754-
logger.debug('Ctrl-C pressed... Exiting...')
755+
logger.debug('Ctrl-C pressed... Terminating...')
755756
self.player.ctrl_c_pressed = True
756757
self.ctrl_c_handler(0, 0)
757758
break
@@ -2894,7 +2895,7 @@ def to_time(secs):
28942895
clean_date_files(files, -1)
28952896
create_tadays_date_file(a_path)
28962897
if logger.isEnabledFor(logging.INFO):
2897-
logger.info('detectUpdateThread: No update found. Will check again in {} days. Exiting...'.format(check_days))
2898+
logger.info('detectUpdateThread: No update found. Will check again in {} days. Terminating...'.format(check_days))
28982899
break
28992900
else:
29002901
# PROGRAM DEBUG: set program's version
@@ -2962,7 +2963,7 @@ def to_time(secs):
29622963
connection_fail_count += 1
29632964
if connection_fail_count > 4:
29642965
if logger.isEnabledFor(logging.ERROR):
2965-
logger.error('detectUpdateThread: Error: Too many connection failures. Exiting...')
2966+
logger.error('detectUpdateThread: Error: Too many connection failures. Terminating...')
29662967
break
29672968
delay(60, stop)
29682969

@@ -3222,6 +3223,12 @@ def _fix_playlist_highlight_after_rename(self, old_file, new_file, copy_file, op
32223223
return ret
32233224

32243225
def keypress(self, char):
3226+
if self._system_asked_to_terminate:
3227+
""" Make sure we exit when signal received """
3228+
if logger.isEnabledFor(logging.debug):
3229+
logger.debug('keypress: Asked to stop. Stoping...')
3230+
return -1
3231+
32253232
if char in (ord('#'), curses.KEY_RESIZE):
32263233
self._normal_mode_resize()
32273234
self._do_display_notify()
@@ -4019,7 +4026,7 @@ def keypress(self, char):
40194026
char not in self._chars_to_bypass and \
40204027
char not in self._chars_to_bypass_for_search and \
40214028
char not in (ord('T'),)):
4022-
logger.error('DE \n\nExiting theme selector?\n\n')
4029+
logger.error('DE \n\nTerminating theme selector?\n\n')
40234030
theme_id, save_theme = self._theme_selector.keypress(char)
40244031

40254032
#if self._cnf.theme_not_supported:
@@ -5426,7 +5433,7 @@ def _show_http_connection(self):
54265433
"""''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
54275434
Windows only section
54285435
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"""
5429-
def _register_windows_handlers(self):
5436+
def _register_signals_handlers(self):
54305437
if platform.startswith('win'):
54315438
""" disable close button """
54325439
import win32console, win32gui, win32con, win32api
@@ -5436,8 +5443,12 @@ def _register_windows_handlers(self):
54365443
if hMenu:
54375444
try:
54385445
win32gui.DeleteMenu(hMenu, win32con.SC_CLOSE, win32con.MF_BYCOMMAND)
5446+
if logger.isEnabledFor(logging.DEBUG):
5447+
logger.debug('SetConsoleCtrlHandler: close button disabled')
54395448
except:
5440-
pass
5449+
if logger.isEnabledFor(logging.DEBUG):
5450+
logger.debug('SetConsoleCtrlHandler: failed to disable close button')
5451+
54415452
""" install handlers for exit / close signals"""
54425453
try:
54435454
result = win32api.SetConsoleCtrlHandler(self._windows_signal_handler, True)
@@ -5451,37 +5462,91 @@ def _register_windows_handlers(self):
54515462
logger.debug('SetConsoleCtrlHandler: Failed to register (with Exception)!!!')
54525463
# Trying to catch Windows log-ogg, reboot, halt
54535464
# No luck....
5454-
#import signal
5455-
#try:
5456-
# signal.signal(signal.SIGINT, self._windows_signal_handler)
5457-
#except:
5458-
# if logger.isEnabledFor(logging.DEBUG):
5459-
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')
5460-
5461-
#try:
5462-
# signal.signal(signal.SIGINT, self._windows_signal_handler)
5463-
#except:
5464-
# if logger.isEnabledFor(logging.DEBUG):
5465-
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')
5465+
# import signal
5466+
# try:
5467+
# signal.signal(signal.SIGINT, self._windows_signal_handler)
5468+
# except:
5469+
# if logger.isEnabledFor(logging.DEBUG):
5470+
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')
5471+
5472+
# try:
5473+
# signal.signal(signal.SIGINT, self._windows_signal_handler)
5474+
# except:
5475+
# if logger.isEnabledFor(logging.DEBUG):
5476+
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')
5477+
5478+
else:
5479+
self.handled_signals = {
5480+
'SIGHUP': signal.SIGHUP,
5481+
'SIGTERM': signal.SIGTERM,
5482+
'SIGKIL': signal.SIGKILL,
5483+
}
5484+
self.def_signal_handlers = {}
5485+
try:
5486+
for a_sig in self.handled_signals.keys():
5487+
self.def_signal_handlers[a_sig] = signal.signal(
5488+
self.handled_signals[a_sig],
5489+
self._linux_signal_handler
5490+
)
5491+
if logger.isEnabledFor(logging.DEBUG):
5492+
logger.debug('SetConsoleCtrlHandler: Handler for signal {} registered'.format(a_sig))
5493+
except:
5494+
if logger.isEnabledFor(logging.DEBUG):
5495+
logger.debug('SetConsoleCtrlHandler: Failed to register handler for signal {}'.format(a_sig))
5496+
5497+
def _linux_signal_handler(self, a_signal, a_frame):
5498+
if self._system_asked_to_terminate:
5499+
return
5500+
self._system_asked_to_terminate = True
5501+
if logger.isEnabledFor(logging.INFO):
5502+
# logger.info('System asked me to terminate (signal: {})!!!'.format(list(self.handled_signals.keys())[list(self.handled_signals.values()).index(a_signal)]))
5503+
logger.info('My terminal got closed... Terminating...')
5504+
self._force_exit = True
5505+
self.stop_update_notification_thread = True
5506+
self.player.stop_timeout_counter_thread = True
5507+
if self.ws.operation_mode != self.ws.PLAYLIST_MODE:
5508+
if self._cnf.dirty_playlist:
5509+
self._cnf.save_playlist_file()
5510+
self.player.close()
5511+
self._cnf.save_config()
5512+
#self._wait_for_threads()
5513+
self._cnf.remove_session_lock_file()
5514+
for a_sig in self.handled_signals.keys():
5515+
try:
5516+
signal.signal(
5517+
self.handled_signals[a_sig],
5518+
self.def_signal_handlers[a_sig]
5519+
)
5520+
except:
5521+
pass
54665522

54675523
def _windows_signal_handler(self, event):
54685524
""" windows signal handler
54695525
https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/
54705526
"""
5471-
import win32con, win32api, signal
5527+
import win32con, win32api
54725528
if event in (win32con.CTRL_C_EVENT,
5473-
win32con.CTRL_LOGOFF_EVENT,
5474-
win32con.CTRL_BREAK_EVENT,
5475-
win32con.CTRL_SHUTDOWN_EVENT,
5476-
win32con.CTRL_CLOSE_EVENT,
5477-
signal.SIGINT,
5478-
signal.SIGBREAK):
5529+
win32con.CTRL_LOGOFF_EVENT,
5530+
win32con.CTRL_BREAK_EVENT,
5531+
win32con.CTRL_SHUTDOWN_EVENT,
5532+
win32con.CTRL_CLOSE_EVENT,
5533+
signal.SIGINT,
5534+
signal.SIGBREAK):
5535+
if self._system_asked_to_terminate:
5536+
return
5537+
self._system_asked_to_terminate = True
5538+
if logger.isEnabledFor(logging.INFO):
5539+
logger.info('My console window got closed... Terminating...')
54795540
self._force_exit = True
5480-
self.ctrl_c_handler(0,0)
5481-
if logger.isEnabledFor(logging.DEBUG):
5482-
logger.debug('Windows asked me to terminate!!')
5541+
self.player.close_from_windows()
5542+
self._cnf.save_config()
5543+
self._wait_for_threads()
5544+
self._cnf.remove_session_lock_file()
5545+
if self.ws.operation_mode != self.ws.PLAYLIST_MODE:
5546+
if self._cnf.dirty_playlist:
5547+
self._cnf.save_playlist_file()
54835548
try:
5484-
result = win32api.SetConsoleCtrlHandler(self._windows_signal_handler, False)
5549+
win32api.SetConsoleCtrlHandler(self._windows_signal_handler, False)
54855550
except:
54865551
pass
54875552
return False

0 commit comments

Comments
 (0)