From 6fbf308a620477f7e5dc9f7923831de54ec6491e Mon Sep 17 00:00:00 2001 From: Spiros Georgaras Date: Thu, 5 Dec 2024 12:49:28 +0200 Subject: [PATCH] - version 0.9.3.11.2 - 0.9.3.12-beta2 - fixing saving keyboard.json - updating docs --- Changelog | 2 +- devel/update_win_players | 7 +- docs/index.html | 2 +- docs/pyradio.1 | 2 + pyradio/config_window.py | 167 ++++++++++++++++++++++--------------- pyradio/keyboard.py | 19 ++--- pyradio/messages_system.py | 2 +- pyradio/radio.py | 2 +- 8 files changed, 121 insertions(+), 82 deletions(-) diff --git a/Changelog b/Changelog index 0bee129..2be3931 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -2024-12-04 s-n-g +2024-12-05 s-n-g * version 0.9.3.11.2 - 0.9.3.12-beta2 * Implementing the Shortcuts Window, making it possible to customize PyRadio's key bindings diff --git a/devel/update_win_players b/devel/update_win_players index dc8d030..b7707a8 100755 --- a/devel/update_win_players +++ b/devel/update_win_players @@ -28,7 +28,12 @@ function get_mplayer(){ echo -en " Reading published ${cMagenta}MPlayer${cReset} versions ... " - FOUND=$(curl -s -L 'https://sourceforge.net/projects/mplayerwin/files/MPlayer-MEncoder' 2>/dev/null | grep '/dev/null | \ + grep 'Requirements Changelog Top
 
-2024-12-04 s-n-g
+2024-12-05 s-n-g
     * version 0.9.3.11.2 - 0.9.3.12-beta2
     * Implementing the Shortcuts Window, making it possible to customize
       PyRadio's key bindings
diff --git a/docs/pyradio.1 b/docs/pyradio.1
index 9c77012..be035e8 100644
--- a/docs/pyradio.1
+++ b/docs/pyradio.1
@@ -30,6 +30,8 @@ not exist. Not available on Windows.
 .IP\ \fB-p\fR\ [\fISTATION_NUMBER\fR],\ \fB--play\fR\ [\fISTATION_NUMBER\fR]
 Start and play. The value is num station or empty for
 random.
+.IP\ \fB-x\fR,\ \fB--external-player\fR
+Play station in external player. Can be combined with \fB--play\fR.
 .IP\ \fB-u\fR\ \fIPLAYER\fR,\ \fB--use\fR\fB-player\fR\ \fIPLAYER\fR
 Use specified player. A comma-separated list can be
 used to specify detection order. Supported players:
diff --git a/pyradio/config_window.py b/pyradio/config_window.py
index f9ed3bb..ba2d356 100644
--- a/pyradio/config_window.py
+++ b/pyradio/config_window.py
@@ -21,7 +21,7 @@
 from .server import IPsWithNumbers
 from .simple_curses_widgets import SimpleCursesLineEdit, SimpleCursesHorizontalPushButtons, SimpleCursesMenu
 from .client import PyRadioClient
-from .keyboard import kbkey, kbkey_orig, ctrl_code_to_string, is_valid_char, is_invalid_key, is_ctrl_key, set_kbkey, conflicts
+from .keyboard import kbkey, kbkey_orig, ctrl_code_to_string, is_valid_char, is_invalid_key, is_ctrl_key, set_kbkey, conflicts, read_keyboard_shortcuts
 locale.setlocale(locale.LC_ALL, '')    # set your locale
 
 logger = logging.getLogger(__name__)
@@ -3407,7 +3407,7 @@ def __init__(
         self._global_functions = global_functions
 
         self._list = []
-        self._dict = OrderedDict()
+        tmp_dict = OrderedDict()
         for i, key in enumerate(kbkey_orig):
             ''' the dict contains
                 [
@@ -3417,24 +3417,27 @@ def __init__(
                     8: title
                 ]
             '''
-            self._dict[key] =  [
+            tmp_dict[key] =  [
                     kbkey_orig[key][0], 0, 0, '', '', '', i, 0, kbkey_orig[key][1]]
         for key in kbkey:
-            self._dict[key][1] = kbkey[key]
-            self._dict[key][2] = self._dict[key][1]
-            self._dict[key][3] = ctrl_code_to_string(self._dict[key][0])
-            self._dict[key][4] = ctrl_code_to_string(self._dict[key][1])
-            self._dict[key][5] = ctrl_code_to_string(self._dict[key][2])
-        for n in self._dict:
-            logger.error(f'{n}: {self._dict[n]}')
+            tmp_dict[key][1] = kbkey[key]
+            tmp_dict[key][2] = tmp_dict[key][1]
+            tmp_dict[key][3] = ctrl_code_to_string(tmp_dict[key][0])
+            tmp_dict[key][4] = ctrl_code_to_string(tmp_dict[key][1])
+            tmp_dict[key][5] = ctrl_code_to_string(tmp_dict[key][2])
+        if logger.isEnabledFor(logging.DEBUG):
+            for n in tmp_dict:
+                logger.debug(f'{n}: {tmp_dict[n]}')
         ''' dct contains
             [0: key, 1:def_code, 2:old_code, 3:new_code, 4:def_string, 5:old_string, 6:new_string, 7:description]
         '''
-        self._list = [[key] + list(value) for key, value in self._dict.items()]
+        self._list = [[key] + list(value) for key, value in tmp_dict.items()]
         self._max_length = max(len(sublist[-1]) for sublist in self._list) + 8
-        logger.error(f'{self._max_length = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{self._max_length = }')
         self._headers = [i for i, x in enumerate(self._list) if x[1] is None]
-        logger.error(f'{self._headers = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{self._headers = }')
 
         # insert header index into self._list
         for x in range(len(self._headers)):
@@ -3442,8 +3445,9 @@ def __init__(
             for i in range(len(self._list)):
                 if i > header_index:
                     self._list[i][-2] = header_index
-        for n in self._list:
-            logger.error(f'{n}')
+        if logger.isEnabledFor(logging.DEBUG):
+            for n in self._list:
+                logger.debug(f'{n}')
         '''
 
         # do not read keys.json to self._keys_to_classes
@@ -3485,7 +3489,8 @@ def _precompute_context_map(self, results):
         return context_map
 
     def item(self, an_item_id=None):
-        logger.debug(f'{an_item_id =  }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{an_item_id =  }')
         if an_item_id is None:
             return self._list[self._selection]
         return self._list[an_item_id]
@@ -3568,8 +3573,9 @@ def _go_bottom(self):
         self._needs_update = True
 
     def _go_down(self, step=1):
-        logger.error(f'go_down: {step = }')
-        logger.error(f'{len(self._list) = }, {self._selection = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'go_down: {step = }')
+            logger.debug(f'{len(self._list) = }, {self._selection = }')
         next_selection = self._selection + step
         if next_selection >= len(self._list) and step > 1:
             if self._selection == len(self._list) -1:
@@ -3579,33 +3585,40 @@ def _go_down(self, step=1):
             return
         if next_selection in self._headers:
             next_selection += 1
-        logger.error(f'{next_selection = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{next_selection = }')
         if next_selection >= len(self._list):
             self._selection = 1
             self._start = 0
             self._needs_update = True
             return
         line = next_selection - self._start + 2
-        logger.error(f'{line = }, {self._number_of_lines = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{line = }, {self._number_of_lines = }')
         if line > self._number_of_lines + 1 :
-            logger.error('GREATER!')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug('GREATER!')
             while line > self._number_of_lines + 1:
                 self._start += (next_selection - self._selection)
                 self._selection = next_selection
                 self._needs_update = True
                 return
         if 2 <  line <= self._number_of_lines + 1:
-            logger.error('=== between')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug('=== between')
             self._unselect_line(self._selection - self._start + 2)
-            logger.error(f'unselecting {self._selection - self._start + 2}')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug(f'unselecting {self._selection - self._start + 2}')
             self._select_line(next_selection - self._start + 2)
-            logger.error(f'selecting {next_selection - self._start + 2}')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug(f'selecting {next_selection - self._start + 2}')
             self._selection = next_selection
             self._win.refresh()
 
     def _go_up(self, step=1):
-        logger.error(f'go_up: {step = }')
-        logger.error(f'{self._selection = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'go_up: {step = }')
+            logger.debug(f'{self._selection = }')
         next_selection = self._selection - step
         if next_selection < 0 and step > 1:
             if self._selection == 1:
@@ -3615,7 +3628,8 @@ def _go_up(self, step=1):
             return
         if next_selection in self._headers:
             next_selection -= 1
-        logger.error(f'{next_selection = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{next_selection = }')
         if next_selection < 0:
             self._selection = len(self._list) - 1
             if len(self._list) <= self._number_of_lines:
@@ -3635,10 +3649,12 @@ def _go_up(self, step=1):
             self._needs_update = True
             return
         if 1 <=  line <= self._number_of_lines:
-            logger.error('=== between')
-            logger.error(f'unselecting {self._selection - self._start + 2}')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug('=== between')
+                logger.debug(f'unselecting {self._selection - self._start + 2}')
             self._unselect_line(self._selection - self._start + 2)
-            logger.error(f'selecting {self._selection - self._start + 2}')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug(f'selecting {self._selection - self._start + 2}')
             self._select_line(next_selection - self._start + 2)
             self._selection = next_selection
             self._win.refresh()
@@ -3669,10 +3685,11 @@ def _init_win(self):
         self._start_line = 1
         self._end_line = self.maxY - 4
         self._number_of_lines = self.maxY - 6
-        logger.error(f'{self.maxY = }')
-        logger.error(f'{self._start_line = }')
-        logger.error(f'{self._end_line = }')
-        logger.error(f'{self._number_of_lines = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{self.maxY = }')
+            logger.debug(f'{self._start_line = }')
+            logger.debug(f'{self._end_line = }')
+            logger.debug(f'{self._number_of_lines = }')
         if self._widget is not None and not self._too_small:
             # move buttons to end of window
             self._widget.move(self.maxY - 3)
@@ -3763,7 +3780,6 @@ def show(self, parent=None):
             if cur < len(self._list):
                 try:
                     if self._list[cur][1] is None:
-                        logger.error(f'=== header at {i+2}')
                         self._win.addstr(i+2, 2, (self.maxX -4) * ' ', curses.color_pair(4))
                         self._win.addstr(i+2, 2, self._list[cur][-1], curses.color_pair(4))
                     else:
@@ -3838,29 +3854,34 @@ def _detect_conflict(self, modified_item):
         key = modified_item[0]  # Identifier for the shortcut (e.g., "reload", "mute")
         new_shortcut_code = modified_item[3]  # The new shortcut code provided by the user
 
-        logger.error('\n\n-*-*-*-*-*-*-*-*-')
-        logger.error(f'{key = }')
-        logger.error(f'{new_shortcut_code = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug('\n\n-*-*-*-*-*-*-*-*-')
+            logger.debug(f'{key = }')
+            logger.debug(f'{new_shortcut_code = }')
 
         # Step 1: Retrieve contexts for the current key
         if key not in self._keys_to_classes:
-            logger.error('\n-*-*-*-*-*-*-*-*- None 1\n\n')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug('\n-*-*-*-*-*-*-*-*- None 1\n\n')
             return
 
         # Collect all relevant keys for this key's contexts
         context_classes = self._keys_to_classes[key]  # List of class names where this key is used
-        logger.error(f'{context_classes = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{context_classes = }')
         context_keys = set()
         for context_class in context_classes:
             for key_in_context in self._keys_to_classes:
                 if context_class in self._keys_to_classes[key_in_context]:
                     context_keys.add(key_in_context)
 
-        logger.error(f'{context_keys = }')
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{context_keys = }')
 
         tmp = [x for x in self._list if x[0] in context_keys]
 
-        logger.error('\n\ntmp\n{}'.format(tmp))
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug('\n\ntmp\n{}'.format(tmp))
 
         # Step 2: Detect conflict within the resolved context keys
         for key_in_context in context_keys:  # Iterate through all relevant keys in the context
@@ -3870,16 +3891,15 @@ def _detect_conflict(self, modified_item):
                 continue
 
             idx, chk = [(i, x) for i, x in enumerate(self._list) if x[0] == key_in_context][0]
-            logger.error('\n\nitem with key  -  {0}: {1}\n\n'.format(idx, chk))
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug('\n\nitem with key  -  {0}: {1}\n\n'.format(idx, chk))
             # Check if the new shortcut code matches the existing shortcut code for any other key
             if chk[3] == new_shortcut_code:
                 self.existing_conflict = (modified_item[-3], idx)  # Return the first conflicting key and index
                 return
 
-        logger.error('\n-*-*-*-*-*-*-*-*- None 2\n\n')
-
-        # No conflict found
-        # self.existing_conflict = None
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug('\n-*-*-*-*-*-*-*-*- None 2\n\n')
 
     def _validate_key(self):
         self._detect_conflict(self._list[self._selection])
@@ -3890,19 +3910,28 @@ def _validate_key(self):
     def _save_keyboard_config(self):
         out_dict = {}
         for i, n in enumerate(self._list):
-            if n[1] != n[2]:
-                out_dict[n[0]] = n[2]
-            if n[2] != n[3]:
+            if n[1] == n[2] == n[3]:
+                continue
+            if n[1] == n[3]:
+                continue
+            if n[3] and n[2] != n[3]:
+                if logger.isEnabledFor(logging.DEBUG):
+                    logger.debug('New shortcut found: {0}: {1}'.format(n[0], n[6]))
                 out_dict[n[0]] = n[3]
+            if n[2] and n[1] != n[2]:
+                if logger.isEnabledFor(logging.DEBUG):
+                    logger.debug('New shortcut found: {0}: {1}'.format(n[0], n[5]))
+                out_dict[n[0]] = n[2]
+        if logger.isEnabledFor(logging.DEBUG):
+            logger.debug(f'{out_dict = }')
         import json
         try:
-            with open(self._cnf.keyboard_file, 'w') as json_file:
+            with open(self._cnf.keyboard_file, 'w', encoding='utf-8') as json_file:
                 json.dump(out_dict, json_file)
         except (FileNotFoundError, TypeError, ValueError, IOError) as e:
             # file save failure
             return -2
-        for n in out_dict:
-            set_kbkey(n, out_dict[n])
+        read_keyboard_shortcuts(self._cnf.keyboard_file, reset=True)
         return 0
 
     def _get_available_keys(self):
@@ -3954,11 +3983,13 @@ def keypress(self, char):
         elif self._editing:
             if is_invalid_key(char):
                 self.message = 'M_INVALID_KEY_ERROR'
-                logger.error('1 Key is INVALID')
+                if logger.isEnabledFor(logging.DEBUG):
+                    logger.debug('1 Key is INVALID')
                 return 2
             if not is_valid_char(char, self._win):
                 self.message = 'M_INVALID_TYPE_KEY_ERROR'
-                logger.error('2 Key is INVALID')
+                if logger.isEnabledFor(logging.DEBUG):
+                    logger.debug('2 Key is INVALID')
                 return 2
             if char in (curses.KEY_EXIT, 27):
                 self._stop_editing()
@@ -3967,19 +3998,21 @@ def keypress(self, char):
             if the_key in conflicts['h_rb_s'] and not is_ctrl_key(char):
                 self.message ='M_NOT_CTRL_KEY_ERROR'
                 return 2
-            logger.error(f'{the_key = }')
-
-            logger.error('\n\n============')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug(f'{the_key = }')
+                logger.debug('\n\n============')
             self._old_key_value = (self._list[self._selection][3], self._list[self._selection][6])
             self._list[self._selection][3] = char
             self._list[self._selection][6] = ctrl_code_to_string(char)
             ret = self._validate_key()
-            logger.error(f'{ret = }')
-            logger.error('============\n\n')
+            if logger.isEnabledFor(logging.DEBUG):
+                logger.debug(f'{ret = }')
+                logger.debug('============\n\n')
             if ret == 1:
                 ''' put char into the list and update screen '''
-                logger.error(f'{self._list[self._selection] = }')
-                logger.error('line is {}'.format(self._selection - self._start + 2))
+                if logger.isEnabledFor(logging.DEBUG):
+                    logger.debug(f'{self._list[self._selection] = }')
+                    logger.debug('line is {}'.format(self._selection - self._start + 2))
                 self._win.addstr(
                         self._selection - self._start + 2,
                         self._max_length + 17,
@@ -4078,11 +4111,11 @@ def keypress(self, char):
             elif char in self._global_functions:
                 self._global_functions[char]()
 
-
-        logger.error('=============')
-        logger.error(f'{self._start = }')
-        logger.error(f'{self._selection = }')
-        logger.error('line = {}'.format(self._selection - self._start + 2))
+        # if logger.isEnabledFor(logging.DEBUG):
+        #     logger.debug('=============')
+        #     logger.debug(f'{self._start = }')
+        #     logger.debug(f'{self._selection = }')
+        #     logger.debug('line = {}'.format(self._selection - self._start + 2))
 
         # Centralized UI update
         if self._needs_update:
diff --git a/pyradio/keyboard.py b/pyradio/keyboard.py
index 15247ef..f1e316d 100644
--- a/pyradio/keyboard.py
+++ b/pyradio/keyboard.py
@@ -250,16 +250,15 @@ def read_keyboard_shortcuts(file_path, reset=False):
     global kbkey  # Declare kbkey as global since we're reassigning it
     if reset:
         kbkey = populate_dict()  # Reassign kbkey with a new OrderedDict
-    else:
-        data = None
-        try:
-            with open(file_path, 'r', encoding='utf-8', errors='ignore') as json_file:
-                data = json.load(json_file)
-        except (FileNotFoundError, json.JSONDecodeError, TypeError, IOError):
-            pass
-        if data is not None:
-            for n in data.keys():
-                kbkey[n] = data[n]  # Modify the existing kbkey
+    data = None
+    try:
+        with open(file_path, 'r', encoding='utf-8', errors='ignore') as json_file:
+            data = json.load(json_file)
+    except (FileNotFoundError, json.JSONDecodeError, TypeError, IOError):
+        pass
+    if data is not None:
+        for n in data.keys():
+            kbkey[n] = data[n]  # Modify the existing kbkey
 
 def read_localized_keyboard(file_path, keyboard_path):
     ''' read file_path which is {'keyboard': 'name of country'},
diff --git a/pyradio/messages_system.py b/pyradio/messages_system.py
index 47a72c2..dd100b8 100644
--- a/pyradio/messages_system.py
+++ b/pyradio/messages_system.py
@@ -1491,7 +1491,7 @@ def set_text(self, parent, *args):
 __|Esc|                                  |*|Exit |editing mode|.
 __|0|                                    |*| Switch between |c|ocnflicting items.
                                          |*|Available in |editing mode| as well.
-__|{h}|                                  |*|Display this help screen.
+__|{?}|                                  |*|Display this help screen.
 ''')) + r'''
 
 To change a |Keyboard Shortcut|, just enter the |editing mode|. This will
diff --git a/pyradio/radio.py b/pyradio/radio.py
index 9554fd4..aae2706 100644
--- a/pyradio/radio.py
+++ b/pyradio/radio.py
@@ -6083,7 +6083,7 @@ def keypress(self, char):
         # # logger.error('\n\nbackup params\n{}\n\n'.format(self._cnf.backup_player_params))
         # if char == curses.KEY_RESIZE:
         #     logger.error('\n\nRESIZE\n\n')
-        logger.error('\n\nchar = {}\n\n'.format(char))
+        # logger.error('\n\nchar = {}\n\n'.format(char))
         # letter = get_unicode_and_cjk_char(self.outerBodyWin, char)
         # logger.error('\n\nletter = {}\n\n'.format(letter))
         if char in (curses.KEY_RESIZE, ):