Skip to content

Commit

Permalink
- Version 0.8.8.2
Browse files Browse the repository at this point in the history
- Gracefully exit when the terminal is closed
  • Loading branch information
s-n-g committed Dec 18, 2020
1 parent 1b20bf8 commit 130ccad
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 60 deletions.
9 changes: 9 additions & 0 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2020-12-18 s-n-g
* Version 0.8.8.2
* Gracefully exit when the terminal is closed

2020-12-14 s-n-g
* Version 0.8.8.1
* Fixing (?) vlc terminataion on Windows
* Restarting radio-browser.info implementation

2020-12-10 s-n-g
* Starting 0.8.8
* Implementing "Paste to playlist" (\p) command
Expand Down
2 changes: 1 addition & 1 deletion pyradio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
" pyradio -- Console radio player. "

version_info = (0, 8, 8, 1)
version_info = (0, 8, 8, 2)

# Application state:
# New stable version: ''
Expand Down
50 changes: 27 additions & 23 deletions pyradio/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,6 @@ def shell():
pyradio_config.force_to_remove_lock_file = True
sys.exit()

# set window title
if platform.startswith('win'):
import ctypes
try:
if pyradio_config.locked:
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player (Session Locked)")
else:
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player")
except:
pass
else:
try:
if pyradio_config.locked:
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player (Session Locked)\x07")
else:
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player\x07")
except:
pass

if args.show_config_dir:
print('PyRadio config dir: "{}"'.format(pyradio_config.stations_dir))
sys.exit()
Expand Down Expand Up @@ -211,13 +192,16 @@ def shell():
theme_to_use = pyradio_config.theme

# Starts the radio TUI.
pyradio = PyRadio(pyradio_config,
play=args.play,
req_player=requested_player,
theme=theme_to_use)
pyradio = PyRadio(
pyradio_config,
play=args.play,
req_player=requested_player,
theme=theme_to_use
)
""" Setting ESCAPE key delay to 25ms
Refer to: https://stackoverflow.com/questions/27372068/why-does-the-escape-key-have-a-delay-in-python-curses"""
environ.setdefault('ESCDELAY', '25')
set_terminal_title()
curses.wrapper(pyradio.setup)
if pyradio.setup_return_status:
print('\nThank you for using PyRadio. Cheers!')
Expand Down Expand Up @@ -267,6 +251,26 @@ def print_playlist_selection_error(a_selection, cnf, ret, exit_if_malformed=True
print('File type not supported')
sys.exit(1)

def set_terminal_title():
# set window title
if platform.startswith('win'):
import ctypes
try:
if pyradio_config.locked:
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player (Session Locked)")
else:
ctypes.windll.kernel32.SetConsoleTitleW("PyRadio: The Internet Radio player")
except:
pass
else:
try:
if pyradio_config.locked:
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player (Session Locked)\x07")
else:
sys.stdout.write("\x1b]2;PyRadio: The Internet Radio player\x07")
except:
pass

def open_conf_dir(cnf):
import subprocess
import os
Expand Down
16 changes: 11 additions & 5 deletions pyradio/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,8 +1155,14 @@ def _sendCommand(self, command):
if logger.isEnabledFor(logging.ERROR):
logger.error(msg.format(command).strip(), exc_info=True)

def close_from_windows(self):
""" kill player instance when window console is closed """
if self.process:
self.close()
self._stop()

def close(self):
""" exit pyradio (and kill player instance) """
""" kill player instance """

self._no_mute_on_stop_playback()

Expand Down Expand Up @@ -1413,7 +1419,7 @@ def pause(self):
self._send_mpv_command('pause')

def _stop(self):
""" exit pyradio (and kill mpv instance) """
""" kill mpv instance """
self.stop_mpv_status_update_thread = True
self._send_mpv_command('quit')
os.system("rm " + self.mpvsocket + " 2>/dev/null");
Expand Down Expand Up @@ -1687,7 +1693,7 @@ def pause(self):
self._sendCommand("p")

def _stop(self):
""" exit pyradio (and kill mplayer instance) """
""" kill mplayer instance """
self._sendCommand("q")
self._icy_data = {}

Expand Down Expand Up @@ -1844,7 +1850,7 @@ def pause(self):
self._sendCommand("stop\n")

def _stop(self):
""" exit pyradio (and kill vlc instance) """
""" kill vlc instance """
logger.error('setting self.stop_win_vlc_status_update_thread = True')
self.stop_win_vlc_status_update_thread = True
if self.ctrl_c_pressed:
Expand All @@ -1853,7 +1859,7 @@ def _stop(self):
if self.process:
logger.error('>>>> Terminating process')
self._req('quit')
threading.Thread(target=self._remove_vlc_stdout_log_file, args=()).start()
threading.Thread(target=self._remove_vlc_stdout_log_file, args=()).start()
else:
self._sendCommand("shutdown\n")
self._icy_data = {}
Expand Down
127 changes: 96 additions & 31 deletions pyradio/radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import logging
import os
import random
#import signal
import signal
from sys import version as python_version, version_info, platform
from os.path import join, basename, getmtime, getsize
from os import remove
Expand Down Expand Up @@ -183,6 +183,7 @@ def ll(self, msg):
logger.error('DE p REGISTER_MODE: {0}, {1}, {2}'.format(*self.playlist_selections[2]))

def __init__(self, pyradio_config, play=False, req_player='', theme=''):
self._system_asked_to_terminate = False
self._cnf = pyradio_config
self._theme = PyRadioTheme(self._cnf)
if theme:
Expand Down Expand Up @@ -694,6 +695,7 @@ def __displayBodyLine(self, lineNum, pad, station):
self.bodyWin.chgat(lineNum, n, 1, sep_col)

def run(self):
self._register_signals_handlers()
if self.ws.operation_mode == self.ws.NO_PLAYER_ERROR_MODE:
if self.requested_player:
if ',' in self.requested_player:
Expand All @@ -707,7 +709,6 @@ def run(self):
except KeyboardInterrupt:
pass
else:
self._register_windows_handlers()

# start update detection and notification thread
if CAN_CHECK_FOR_UPDATES:
Expand Down Expand Up @@ -751,7 +752,7 @@ def run(self):
return
except KeyboardInterrupt:
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Ctrl-C pressed... Exiting...')
logger.debug('Ctrl-C pressed... Terminating...')
self.player.ctrl_c_pressed = True
self.ctrl_c_handler(0, 0)
break
Expand Down Expand Up @@ -2894,7 +2895,7 @@ def to_time(secs):
clean_date_files(files, -1)
create_tadays_date_file(a_path)
if logger.isEnabledFor(logging.INFO):
logger.info('detectUpdateThread: No update found. Will check again in {} days. Exiting...'.format(check_days))
logger.info('detectUpdateThread: No update found. Will check again in {} days. Terminating...'.format(check_days))
break
else:
# PROGRAM DEBUG: set program's version
Expand Down Expand Up @@ -2962,7 +2963,7 @@ def to_time(secs):
connection_fail_count += 1
if connection_fail_count > 4:
if logger.isEnabledFor(logging.ERROR):
logger.error('detectUpdateThread: Error: Too many connection failures. Exiting...')
logger.error('detectUpdateThread: Error: Too many connection failures. Terminating...')
break
delay(60, stop)

Expand Down Expand Up @@ -3222,6 +3223,12 @@ def _fix_playlist_highlight_after_rename(self, old_file, new_file, copy_file, op
return ret

def keypress(self, char):
if self._system_asked_to_terminate:
""" Make sure we exit when signal received """
if logger.isEnabledFor(logging.debug):
logger.debug('keypress: Asked to stop. Stoping...')
return -1

if char in (ord('#'), curses.KEY_RESIZE):
self._normal_mode_resize()
self._do_display_notify()
Expand Down Expand Up @@ -4019,7 +4026,7 @@ def keypress(self, char):
char not in self._chars_to_bypass and \
char not in self._chars_to_bypass_for_search and \
char not in (ord('T'),)):
logger.error('DE \n\nExiting theme selector?\n\n')
logger.error('DE \n\nTerminating theme selector?\n\n')
theme_id, save_theme = self._theme_selector.keypress(char)

#if self._cnf.theme_not_supported:
Expand Down Expand Up @@ -5426,7 +5433,7 @@ def _show_http_connection(self):
"""''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Windows only section
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"""
def _register_windows_handlers(self):
def _register_signals_handlers(self):
if platform.startswith('win'):
""" disable close button """
import win32console, win32gui, win32con, win32api
Expand All @@ -5436,8 +5443,12 @@ def _register_windows_handlers(self):
if hMenu:
try:
win32gui.DeleteMenu(hMenu, win32con.SC_CLOSE, win32con.MF_BYCOMMAND)
if logger.isEnabledFor(logging.DEBUG):
logger.debug('SetConsoleCtrlHandler: close button disabled')
except:
pass
if logger.isEnabledFor(logging.DEBUG):
logger.debug('SetConsoleCtrlHandler: failed to disable close button')

""" install handlers for exit / close signals"""
try:
result = win32api.SetConsoleCtrlHandler(self._windows_signal_handler, True)
Expand All @@ -5451,37 +5462,91 @@ def _register_windows_handlers(self):
logger.debug('SetConsoleCtrlHandler: Failed to register (with Exception)!!!')
# Trying to catch Windows log-ogg, reboot, halt
# No luck....
#import signal
#try:
# signal.signal(signal.SIGINT, self._windows_signal_handler)
#except:
# if logger.isEnabledFor(logging.DEBUG):
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')

#try:
# signal.signal(signal.SIGINT, self._windows_signal_handler)
#except:
# if logger.isEnabledFor(logging.DEBUG):
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')
# import signal
# try:
# signal.signal(signal.SIGINT, self._windows_signal_handler)
# except:
# if logger.isEnabledFor(logging.DEBUG):
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')

# try:
# signal.signal(signal.SIGINT, self._windows_signal_handler)
# except:
# if logger.isEnabledFor(logging.DEBUG):
# logger.debug('SetConsoleCtrlHandler: Signal SIGINT failed to register (with Exception)!!!')

else:
self.handled_signals = {
'SIGHUP': signal.SIGHUP,
'SIGTERM': signal.SIGTERM,
'SIGKIL': signal.SIGKILL,
}
self.def_signal_handlers = {}
try:
for a_sig in self.handled_signals.keys():
self.def_signal_handlers[a_sig] = signal.signal(
self.handled_signals[a_sig],
self._linux_signal_handler
)
if logger.isEnabledFor(logging.DEBUG):
logger.debug('SetConsoleCtrlHandler: Handler for signal {} registered'.format(a_sig))
except:
if logger.isEnabledFor(logging.DEBUG):
logger.debug('SetConsoleCtrlHandler: Failed to register handler for signal {}'.format(a_sig))

def _linux_signal_handler(self, a_signal, a_frame):
if self._system_asked_to_terminate:
return
self._system_asked_to_terminate = True
if logger.isEnabledFor(logging.INFO):
# logger.info('System asked me to terminate (signal: {})!!!'.format(list(self.handled_signals.keys())[list(self.handled_signals.values()).index(a_signal)]))
logger.info('My terminal got closed... Terminating...')
self._force_exit = True
self.stop_update_notification_thread = True
self.player.stop_timeout_counter_thread = True
if self.ws.operation_mode != self.ws.PLAYLIST_MODE:
if self._cnf.dirty_playlist:
self._cnf.save_playlist_file()
self.player.close()
self._cnf.save_config()
#self._wait_for_threads()
self._cnf.remove_session_lock_file()
for a_sig in self.handled_signals.keys():
try:
signal.signal(
self.handled_signals[a_sig],
self.def_signal_handlers[a_sig]
)
except:
pass

def _windows_signal_handler(self, event):
""" windows signal handler
https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/
"""
import win32con, win32api, signal
import win32con, win32api
if event in (win32con.CTRL_C_EVENT,
win32con.CTRL_LOGOFF_EVENT,
win32con.CTRL_BREAK_EVENT,
win32con.CTRL_SHUTDOWN_EVENT,
win32con.CTRL_CLOSE_EVENT,
signal.SIGINT,
signal.SIGBREAK):
win32con.CTRL_LOGOFF_EVENT,
win32con.CTRL_BREAK_EVENT,
win32con.CTRL_SHUTDOWN_EVENT,
win32con.CTRL_CLOSE_EVENT,
signal.SIGINT,
signal.SIGBREAK):
if self._system_asked_to_terminate:
return
self._system_asked_to_terminate = True
if logger.isEnabledFor(logging.INFO):
logger.info('My console window got closed... Terminating...')
self._force_exit = True
self.ctrl_c_handler(0,0)
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Windows asked me to terminate!!')
self.player.close_from_windows()
self._cnf.save_config()
self._wait_for_threads()
self._cnf.remove_session_lock_file()
if self.ws.operation_mode != self.ws.PLAYLIST_MODE:
if self._cnf.dirty_playlist:
self._cnf.save_playlist_file()
try:
result = win32api.SetConsoleCtrlHandler(self._windows_signal_handler, False)
win32api.SetConsoleCtrlHandler(self._windows_signal_handler, False)
except:
pass
return False
Expand Down

0 comments on commit 130ccad

Please sign in to comment.