diff --git a/Changelog b/Changelog index 3dcd7e0..0bee129 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,10 @@ +2024-12-04 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 + * fixing install.py (#262) + * implementing #252 + 2024-10-17 s-n-g * version 0.9.3.11.1 (BUG FIX) - 0.9.3.12-beta1 * adding -sdd (--show_dirs) command line parameter diff --git a/README.md b/README.md index 018b017..de74b54 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Command line internet radio player. - Station editor (add/edit) with [CJK characters support](#cjk-characters-support) - Configuration editor - Search function + - Customizable key bindings - Easy installation / updating - Runs on Linux, macOS and Windows @@ -65,7 +66,9 @@ In any other case you will have to [build it from source](docs/build.md). **Note:** Please avoid installing **PyRadio** via **pip**. I (user [s-n-g](https://github.com/s-n-g) @ github) am not the creator of this project, nor do I maintain it on [The Python Package Index (PyPI)](https://pypi.org/project/pyradio/). As a result, the version available there is outdated and I cannot provide any support for it. \ \ -Furthermore, please refrain from using any third-party packaging methods, such as **Snap** or **AppImage**. I am not affiliated with these services or projects, and I cannot guarantee the functionality or version of **PyRadio** provided through them. Additionally, I am unable to offer support for any issues related to these packaging methods. +Furthermore, please refrain from using any third-party packaging methods, such as **Snap** or **AppImage**. I am not affiliated with these services or projects, and I cannot guarantee the functionality or version of **PyRadio** provided through them. Additionally, I am unable to offer support for any issues related to these packaging methods. \ +\ +In any of the above cases, if you do ask for help, I will ask you to install your distro package (or build **PyRadio** from source), before attempting to provide any further help. ## Basic usage diff --git a/devel/what_tag b/devel/what_tag index 22bd402..2f85a2c 100755 --- a/devel/what_tag +++ b/devel/what_tag @@ -10,7 +10,7 @@ cYellow="[1;3${colorYellow}m"${cBold} echo -en "pyproject.toml ${cGreen}" grep version pyproject.toml -echo -en "${cReset}__init.py ${cRed}" +echo -en "${cReset}__init__.py ${cRed}" grep '^version_info ' pyradio/__init__.py echo -en "${cReset}install.py ${cYellow}" grep '^PyRadioInstallPyReleaseVersion ' pyradio/install.py @@ -58,4 +58,15 @@ and execute ${cRed}./add-pkgver${cReset} to get AUR package ready for update. " +echo "Running ${cGreen}./devel/get_shortcuts_and_classes.py${cReset}" +python ./devel/get_shortcuts_and_classes.py + +chk=$(LANG=en_US.UTF-8 git st | grep modified) +if [ ! -z "${chk}" ] +then + echo "Not commited files:" + echo "${cRed}${chk}${cReset}" + echo "Please commit them and try again" + exit 1 +fi diff --git a/docs/index.html b/docs/index.html index 1816f0d..206f96c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -187,8 +187,8 @@
-2024-10-17 s-n-g - * version 0.9.3.11.2 (BUG FIX) - 0.9.3.12-beta2 +2024-12-04 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 * fixing install.py (#262) diff --git a/docs/index.md b/docs/index.md index 90b95d1..80cd6c5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,6 +13,7 @@ Command line internet radio player. * [Command line options](#command-line-options) * [Controls](#controls) * [Global shortcuts](#global-shortcuts) + * [Customizing key bindings](#customizing-key-bindings) * [HTML help](#html-help) * [PyRadio Modes](#pyradio-modes) * [Secondary Modes](#secondary-modes) @@ -71,6 +72,7 @@ Command line internet radio player. * [Update notification](#update-notification) * [Remote Control Server](#remote-control-server) * [Remote Control Client](#remote-control-client) +* [Playing a station in the terminal](#playing-a-station-in-the-terminal) * [Debug mode](#debug-mode) * [Reporting bugs](#reporting-bugs) * [Packaging PyRadio](#packaging-pyradio) @@ -90,6 +92,7 @@ Command line internet radio player. - Station editor (add/edit) with [CJK characters support](#cjk-characters-support) - Configuration editor - Search function + - Customizable key bindings - Easy installation / updating - Runs on Linux, macOS and Windows @@ -126,9 +129,9 @@ Furthermore, please refrain from using any third-party packaging methods, such a ``` # pyradio -h -Usage: pyradio [-h] [-c CONFIG_DIR] [-p [STATION_NUMBER]] [-u PLAYER] [-l] - [-lt] [-sds] [-sd] [-od] [-pc] [-d] [-ul] [-us] [-U] [-R] [-V] - [-ls] [-s PLAYLIST] [-tlp] [-t THEME] [--show-themes] +Usage: pyradio [-h] [-c CONFIG_DIR] [-p [STATION_NUMBER]] [-x] [-u PLAYER] + [-l] [-lt] [-sds] [-sd] [-od] [-pc] [-d] [-ul] [-us] [-U] [-R] + [-V] [-ls] [-s PLAYLIST] [-tlp] [-t THEME] [--show-themes] [--no-themes] [--write-theme IN_THEME OUT_THEME,] [--terminal TERMINAL] [--terminal-param TERMINAL_PARAM] [-oc] [-sc] [-cc] [-gc] [-r] [-or] [-lr] [-mkv MKV_FILE] @@ -146,6 +149,9 @@ General options: -p [STATION_NUMBER], --play [STATION_NUMBER] Start and play.The value is num station or empty for random. + -x, --external-player + Play station in external player. Can be combined with + --play. -u PLAYER, --use-player PLAYER Use specified player. A comma-separated list can be used to specify detection order. Supported players: @@ -247,6 +253,9 @@ Headless operation: ## Controls +The following list shows a **default** key bindings list used within the program. + + Main window Playlists window Themes window ------------------------------------------------------------------------------------------------------------------------------------- Up/Down/j/k/ @@ -316,6 +325,12 @@ When focus is on a "*Line editor*", all shortcuts will work when preceded by a " [2] Function not available in the **RadioBrowser** Search window. +### Customizing key bindings + +**PyRadio** provides the possibility to customize the key bindings above. + +Just open the configuration window and navigate to **Keyboard Shortcuts**. Please do read the help screen provided therein (press "*?" to get to it). + ## HTML help While in **PyRadio** main window, one can open the HTML (offline) help using "**\\h**". @@ -1171,6 +1186,18 @@ If you'd like to set up a "headless" **PyRadio** operation for your linux box, p For more information, please refer to [the relevant page](client.md). +## Playing a station in the terminal + +A user request [Shortcut to quit pyradio and launch standalone player (e.g. mpv) with currently selected station](https://github.com/coderholic/pyradio/issues/252) lead to the possibility to use any player in the terminal. + +This action will be triggered by pressing "*X*". + +After the player stops, **PyRadio** will stop as well. + +**Note:** On Windows, **mpv** and **VLC** will open a new player window instead of executing in the terminal. + +In addition, a command line parameter has been added "*-x*" ("*--exteranl-player*") which when used in conjuction with the "*-p*" ("*--play*") command line parameter, will instruct **PyRadio** to play a station and terminate after the playback stops. + ## Debug mode Adding the "**-d**" option to the command line will instruct **PyRadio** to enter *Debug mode*, which means that it will print debug messages to a file. This file will always reside in the user's home directory and will be named *pyradio.log*. diff --git a/docs/pyradio.1 b/docs/pyradio.1 index 22ca62c..9c77012 100644 --- a/docs/pyradio.1 +++ b/docs/pyradio.1 @@ -2,7 +2,7 @@ .\" Copyright (C) 2018-2024 Spiros Georgaras.\" This manual is freely distributable under the terms of the GPL. .\" -.TH pyradio 1 "June 2024" pyradio +.TH pyradio 1 "December 2024" pyradio .SH Name .PP @@ -1950,6 +1950,16 @@ If so, a notification message will be displayed, informing the user about it and \fBpyradio\fR will uninstall all previously installed versions when updated (using the \fB-U\fR command line parameter), so no extra steps are needed any more to house keep your system. +.SH Playing a station in the terminal + +A user request \fBShortcut to quit pyradio and launch standalone player (e.g. mpv) with currently selected station\fR (\fIhttps://github.com/coderholic/pyradio/issues/252\fR) lead to the possibility to use any player in the terminal. + +This action will be triggered by pressing "\fIX\fR". + +After the player stops, \fBpyradio\fR will stop as well. + +In addition, a command line parameter has been added "\fI-x\fR" ("\fI--exteranl-player\fR") which when used in conjuction with the "\fI-p\fR" ("\fI--play\fR") command line parameter, will instruct \fBpyradio\fR to play a station and terminate after the playback stops. + .SH Debug Mode .PP Adding the \fB-d\fR option to the command line will instruct \fBpyradio\fR to enter \fBDebug mode\fR, which means that it will print debug messages to a file. This file will always reside in the user's home directory and will be named \fIpyradio.log\fR. diff --git a/pyproject.toml b/pyproject.toml index 8998c9c..661bb48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyradio" -version = "0.9.3.11.1" +version = "0.9.3.11.2" authors = [ { name="Ben Dowling", email="ben.m.dowling@gmail.com" }, { name="Spiros Georgaras", email="sng@hellug.gr" }, diff --git a/pyradio/__init__.py b/pyradio/__init__.py index abbda89..f266746 100644 --- a/pyradio/__init__.py +++ b/pyradio/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- " pyradio -- Console radio player. " -version_info = (0, 9, 3, 11, 1) +version_info = (0, 9, 3, 11, 2) # Set it to True if new stations have been # added to the package's stations.csv diff --git a/pyradio/config_window.py b/pyradio/config_window.py index d7c4c63..f9ed3bb 100644 --- a/pyradio/config_window.py +++ b/pyradio/config_window.py @@ -3514,17 +3514,12 @@ def _rename_keyboard_json_file(file_path): return new_file_name def _start_editing(self): + self.existing_conflict = None self._win.addstr(self._selection - self._start + 2, self.maxX-8, '[edit]', curses.color_pair(6)) self._win.refresh() self._editing = True if logger.isEnabledFor(logging.DEBUG): logger.debug('editing "{}"'.format(self._list[self._selection])) - for i in range(self._selection, -1, -1): - if self._list[i][1] is None: - self._in_group = i - break - if logger.isEnabledFor(logging.DEBUG): - logger.debug(f'editing in group "{self._list[self._in_group][0]}"') def _stop_editing(self): self._win.addstr(self._selection - self._start + 2, self.maxX-8, ' ', curses.color_pair(6)) @@ -3760,7 +3755,7 @@ def show(self, parent=None): self._b_ok, self._b_cancel = self._widget.buttons self._b_ok.focused = self._b_cancel.focused = False - self._win.addstr(1, 2, 'Shortcuts', curses.color_pair(12)) + self._win.addstr(1, 2, 'Actions', curses.color_pair(12)) self._win.addstr(1, self._max_length-3, 'Default User New', curses.color_pair(12)) for i in range(0, self._number_of_lines): @@ -3921,6 +3916,7 @@ def _get_available_keys(self): # Separate characters into categories digits = sorted([char for char in available_characters if char.isdigit()]) # Sort digits + digits.pop(0) letters = sorted([char for char in available_characters if char.isalpha()]) # Sort letters function_keys_list = sorted([char for char in available_characters if char.startswith('F')]) # Sort function keys if 'F' in function_keys_list: @@ -3974,6 +3970,7 @@ def keypress(self, char): logger.error(f'{the_key = }') logger.error('\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() @@ -3994,8 +3991,8 @@ def keypress(self, char): ''' disable editing ''' self._stop_editing() else: - self._list[self._selection][3] = self._list[self._selection][2] - self._list[self._selection][6] = self._list[self._selection][5] + # restore previous values on conflict + self._list[self._selection][3], self._list[self._selection][6] = self._old_key_value return ret else: @@ -4008,6 +4005,16 @@ def keypress(self, char): self.keys_string = msg + self._get_available_keys() + '\n\n' return -4 + elif char == kbkey['revert_def']: + for i in range(len(self._list)): + self._list[i][3] = self._list[i][1] + self._list[i][6] = self._list[i][4] + self._needs_update = True + elif char == kbkey['revert_saved']: + for i in range(len(self._list)): + self._list[i][3] = self._list[i][2] + self._list[i][6] = self._list[i][5] + self._needs_update = True elif char == ord('x'): self._list[self._selection][3] = self._list[self._selection][2] self._list[self._selection][6] = self._list[self._selection][5] @@ -4030,8 +4037,8 @@ def keypress(self, char): self._go_down(step=5) elif char == curses.KEY_PPAGE: self._go_up(step=5) - elif char in (curses.KEY_EXIT, 27, kbkey['q']): - return -1 + # elif char in (curses.KEY_EXIT, 27, kbkey['q']): + # return -1 elif char in (curses.KEY_RIGHT, kbkey['l']): if self._focus > 0: self._focus_next() diff --git a/pyradio/install.py b/pyradio/install.py index c43cbc2..70da147 100644 --- a/pyradio/install.py +++ b/pyradio/install.py @@ -17,7 +17,7 @@ ''' This is PyRadio version this install.py was released for ''' -PyRadioInstallPyReleaseVersion = '0.9.3.11.1' +PyRadioInstallPyReleaseVersion = '0.9.3.11.2' locale.setlocale(locale.LC_ALL, "") diff --git a/pyradio/keyboard.py b/pyradio/keyboard.py index fd3a470..15247ef 100644 --- a/pyradio/keyboard.py +++ b/pyradio/keyboard.py @@ -436,6 +436,8 @@ def is_valid_char(char, win): Returns: bool: True if c is valid, False otherwise. """ + if char in (9, ord('\t')): + return False # if char <= 127: if (65 <= char <= 90) or (97 <= char <= 122) or (1 <= char <= 47): ''' 1 byte ''' @@ -456,6 +458,9 @@ def is_valid_char(char, win): elif char in ( ord('='), ord('.'), ord('+'), ord('`'), ord('-'), + ord('1'), ord('2'), ord('3'), + ord('4'), ord('5'), ord('6'), + ord('7'), ord('8'), ord('9'), curses.KEY_F1, curses.KEY_F2, curses.KEY_F3, diff --git a/pyradio/keyboard/classes.json b/pyradio/keyboard/classes.json index abdd89c..bc63160 100644 --- a/pyradio/keyboard/classes.json +++ b/pyradio/keyboard/classes.json @@ -1 +1 @@ -{"SimpleCursesString": ["pause", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesDate": ["t", "q", "tab", "l", "?", "stab", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesTime": ["t", "q", "tab", "l", "pause", "?", "stab", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesCounter": ["q", "l", "?", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesWidgetColumns": ["g", "q", "j", "l", "k", "pause", "G", "?", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesMenu": ["edit", "g", "goto_playing", "q", "add", "j", "l", "k", "pause", "G", "?", "del", "screen_middle", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesCheckBox": ["pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesPushButton": ["pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesLineEdit": ["tab", "paste", "?", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesBoolean": ["q", "l", "pause", "?", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SelectPlayer": ["q", "j", "l", "k", "s", "pause", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadio": ["F8", "https", "repaint", "g", "rb_vote", "open_enc", "extra_p_pamars", "G", "F9", "F7", "open_online", "jump", "gr_prev", "search", "random", "revert_def", "edit", "open_extra", "add_to_reg", "t", "q", "add", "rec", "search_next", "open_config", "s", "pause", "rb_sort", "transp", "search_prev", "ext_player", "append", "screen_top", "Y", "info", "goto_playing", "gr", "st_dn", "no_show", "paste", "n", "revert_saved", "gr_next", "F10", "st_up", "Reload", "fav", "rb_info", "reload", "j", "y", "k", "rb_server", "l", "?", "screen_bottom", "screen_middle", "del", "open_playlist", "open_regs", "h", "hist_prev", "hist_next", "p_prev", "p_next", "rb_p_first", "rb_p_next", "rb_p_prev", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioThemeSelector": ["edit", "g", "add", "reload", "j", "l", "k", "q", "s", "pause", "G", "watch_theme", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserConfigWindow": ["revert_def", "q", "tab", "j", "l", "k", "s", "pause", "?", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserSearchWindow": ["g", "tab", "G", "rb_h_add", "stab", "rb_h_next", "q", "s", "pause", "rb_h_def", "rb_h_save", "rb_h_0", "j", "l", "k", "?", "rb_h_prev", "h", "rb_h_del", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserSort": ["g", "q", "j", "l", "k", "pause", "G", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserServersSelect": ["q", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserServers": ["g", "q", "j", "l", "k", "pause", "G", "?", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserTermNavigator": ["g", "next", "q", "l", "prev", "pause", "G", "?", "del", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioMessagesSystem": ["G", "j", "k", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSimpleScheduleWindow": ["info", "q", "tab", "j", "l", "k", "pause", "?", "stab", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioConfigWindow": ["pause", "l", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraParametersEditor": ["q", "tab", "s", "?", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraParameters": ["q", "l", "s", "pause", "?", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectPlayer": ["tab", "q", "j", "l", "k", "s", "pause", "revert_saved", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectEncodings": ["revert_def", "g", "q", "j", "l", "k", "s", "G", "pause", "revert_saved", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectPlaylist": ["g", "q", "j", "l", "k", "pause", "G", "screen_middle", "revert_saved", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectStation": ["revert_saved", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioKeyboardConfig": ["g", "q", "tab", "j", "l", "k", "G", "pause", "?", "stab", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioEditor": ["q", "tab", "s", "stab", "revert_saved", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioRecordingDir": ["q", "tab", "l", "s", "pause", "?", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioResourceOpener": ["revert_def", "q", "tab", "s", "?", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioRenameFile": ["q", "tab", "l", "s", "pause", "stab", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioBuffering": ["no_buffer", "q", "j", "k", "s", "revert_saved", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioConnectionType": ["q", "j", "l", "k", "s", "pause", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioServerWindow": ["revert_def", "j", "l", "k", "s", "pause", "revert_saved", "h", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "InfoWindow": ["info_rename", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraKeys": ["last_playlist", "open_buffer", "clear_all_reg", "clear_reg", "buffer", "open_dirs", "open_remote_control", "new_playlist", "hist_top", "unnamed", "html_help", "rename_playlist", "change_player"]} \ No newline at end of file +{"SelectPlayer": ["q", "h", "k", "pause", "s", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadio": ["screen_middle", "goto_playing", "open_config", "rb_sort", "open_enc", "pause", "rb_info", "extra_p_pamars", "F9", "repaint", "s", "j", "t", "transp", "add", "info", "n", "y", "G", "k", "?", "no_show", "open_regs", "screen_top", "screen_bottom", "revert_saved", "jump", "st_up", "gr", "append", "g", "search_next", "st_dn", "add_to_reg", "gr_next", "F10", "random", "open_online", "F7", "rec", "paste", "del", "l", "reload", "fav", "open_playlist", "search", "Reload", "rb_server", "ext_player", "q", "h", "gr_prev", "Y", "F8", "edit", "search_prev", "rb_vote", "revert_def", "https", "open_extra", "hist_prev", "hist_next", "p_prev", "p_next", "rb_p_first", "rb_p_next", "rb_p_prev", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioConfigWindow": ["h", "l", "pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraParametersEditor": ["tab", "q", "?", "stab", "s", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraParameters": ["q", "h", "?", "pause", "s", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectPlayer": ["tab", "q", "h", "k", "pause", "revert_saved", "s", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectEncodings": ["G", "h", "q", "k", "pause", "revert_saved", "s", "l", "revert_def", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectPlaylist": ["screen_middle", "q", "h", "G", "k", "pause", "revert_saved", "l", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSelectStation": ["revert_saved", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioKeyboardConfig": ["tab", "G", "q", "h", "k", "?", "pause", "revert_saved", "stab", "revert_def", "l", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioEditor": ["tab", "q", "revert_saved", "stab", "s", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioRecordingDir": ["tab", "q", "?", "pause", "stab", "s", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioResourceOpener": ["tab", "q", "?", "stab", "s", "revert_def", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioRenameFile": ["tab", "q", "pause", "stab", "s", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioBuffering": ["q", "h", "k", "revert_saved", "s", "j", "no_buffer", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioConnectionType": ["q", "h", "k", "pause", "s", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioServerWindow": ["h", "k", "pause", "revert_saved", "s", "revert_def", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioSimpleScheduleWindow": ["tab", "info", "q", "h", "k", "?", "pause", "stab", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioThemeSelector": ["G", "q", "h", "k", "watch_theme", "edit", "pause", "s", "l", "reload", "j", "g", "add", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesString": ["l", "pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesDate": ["tab", "q", "h", "?", "stab", "l", "t", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesTime": ["tab", "q", "h", "?", "pause", "stab", "l", "t", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesCounter": ["q", "h", "?", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesWidgetColumns": ["q", "G", "h", "k", "?", "pause", "l", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesMenu": ["screen_middle", "goto_playing", "q", "h", "G", "k", "?", "edit", "pause", "del", "l", "j", "g", "add", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesCheckBox": ["pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesPushButton": ["pause", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesLineEdit": ["tab", "stab", "paste", "?", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "SimpleCursesBoolean": ["q", "h", "?", "pause", "l", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserConfigWindow": ["tab", "q", "k", "?", "pause", "stab", "s", "revert_def", "l", "j", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserSearchWindow": ["pause", "rb_h_def", "s", "j", "tab", "G", "k", "rb_h_add", "?", "stab", "rb_h_next", "rb_h_prev", "g", "rb_h_save", "l", "rb_h_del", "q", "h", "rb_h_0", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserSort": ["q", "h", "G", "k", "pause", "l", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserServersSelect": ["q", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserServers": ["q", "h", "G", "k", "?", "pause", "l", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "RadioBrowserTermNavigator": ["prev", "q", "h", "G", "?", "pause", "next", "del", "l", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "PyRadioMessagesSystem": ["G", "k", "j", "g", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "InfoWindow": ["info_rename", "tag", "t_tag", "transp", "v_up1", "v_up2", "v_up3", "v_dn1", "v_dn2", "mute", "s_vol", "t_calc_col", "repaint"], "ExtraKeys": ["open_dirs", "buffer", "open_buffer", "hist_top", "clear_all_reg", "html_help", "unnamed", "change_player", "new_playlist", "open_remote_control", "last_playlist", "rename_playlist", "clear_reg"]} \ No newline at end of file diff --git a/pyradio/messages_system.py b/pyradio/messages_system.py index d172e81..47a72c2 100644 --- a/pyradio/messages_system.py +++ b/pyradio/messages_system.py @@ -1467,37 +1467,65 @@ def set_text(self, parent, *args): )), 'M_KEYBOARD_HELP':('Keyboard Shortcuts Help', -kb2str(r''' -This is the |Keyboard Shortcuts| configuration window help. - -The window will display a list of the existing shortcuts: -The first column will display the action the shortcut -corresponds to, the second column (|Default|) will print -the default (hardcoded) shortcut (press |{revert_def}| to activate -them), the third column (|User|) will print the shortcut -the user has already set for the action (press |{revert_saved}| to -activate them), and the last column (|New|) will print the -latest changes, which have not been saved yet. - -Use |Arrow Keys|, |{j}|, |{k}|, |PgUp| and |PgDown| to move, |{g}| and |{G}| to -go to the beginning or the end of the list, or |[| and |]| to -navigate the shortcut groups. - -To change a |Keyboard Shortcut|, select a shortcut, press -|Right|, |{l}|, |Enter|, or |{pause}|. Then a "|[edit]|" will appear -at the right of the line. Press any key to change the -shortcut, or |Esc| to cancel the operation. - -When in navigation mode (not editing a shortcut), press -"|x|" to remove a |New| shortcut previously inserted. Press -"|f|" to see a list of "|free|" keys that you can use. - -After you have finished customizing the shortcuts, press -|OK| (use |Tab| or |{tab}| to navigate). Then |PyRadio| will try to -detect any conflicts, and either help you to resolve -them or save and activate your new shortcuts. +r''' +The |Keyboard Shortcuts| window will display a list of shortcuts in +four columns: +__|Actions| Available actions. +__|Default| The default key for the item. +__|User|___ User custom key for the item (saved value). +__|New|____ Latest change (not saved yet). + +The following action are available: +''' + self._format_columns(kb2str( +r''' +__|Arrow Keys|, |{j}|, |{k}|, |*| +__|PgUp|, |PgDown| |*| Move up, down, etc. +__|Tab|, |{tab}| / |Sh-Tab|, |{stab}| |*| Move to next / previous field. +__|{g}| / |{G}| |*| Go to top / bottom of the list. +__|[| / |]| |*| Move between sections. +__|{revert_def}| / |{revert_saved}| |*| Revert to default / saved shortcuts. +__|x| |*| Revert current item to saved value. +__|f| |*| Show |free| keys. +__|Enter|, |{pause}|, |*| +__|Right|, |{l}| |*| Enter |editing mode| (insert new shortcut). +__|Esc| |*|Exit |editing mode|. +__|0| |*| Switch between |c|ocnflicting items. + |*|Available in |editing mode| as well. +__|{h}| |*|Display this help screen. +''')) + r''' + +To change a |Keyboard Shortcut|, just enter the |editing mode|. This will +be indicated by a "|[edit]|" appearing at the right of the line. Press +any key to change the shortcut, or |Esc| to cancel the operation. + +After you have finished customizing the shortcuts, navigate to the |OK| +button to save and activate your new shortcuts. + +Keep in mind that this is the only window in |PyRadio| that will not be +closed when "|Esc|" is pressed; you will have to navigate to the |Cancel| +button and press it, instead. + +|Important Notice on Shortcut Customization + +As you customize your shortcuts, please be aware that adding a new +shortcut triggers a |validation| procedure. + +The system is designed to be |context-aware|; if the new key you choose +is already in use, it will check whether it conflicts within the same +context. In such cases, an error message will be displayed, and the +change will be rejected. + +However, we recognize that there may be instances where conflicting +keys go undetected by the system. We kindly ask you to keep an eye +out for any such conflicts. If you encounter a situation where a +shortcut |seems to be causing issues| without triggering a validation +error, please |report| this incident to us, at this URL: + +____|https://github.com/coderholic/pyradio/issues + +Thank you for your cooperation. ''' -)), +), 'M_INVALID_KEY_ERROR':('Invalid Key', r''' @@ -1971,6 +1999,60 @@ def erase(self): self._win.erase() self._win.refresh() + def _format_columns(self, help_text): + # Step 1: Use the original help text + # help_text = self.help_text + + # Step 2: Split lines and process each line + lines = help_text.strip().split('\n') + formatted_lines = [] + max_left_length = 0 + + for line in lines: + # Split by '|*|' and strip whitespace + parts = [part.strip() for part in line.split('|*|')] + if len(parts) != 2: + continue # Skip lines that don't conform to expected format + + left_part = parts[0].strip() # Keep '|' in left part + right_part = parts[1].strip() # Right part remains as is + + # Calculate lengths + left_length_no_pipe = len(left_part.replace('|', '').strip()) + left_length_with_pipe = len(left_part.strip()) + + # Update max length of left part without pipes + max_left_length = max(max_left_length, left_length_no_pipe) + + # Prepare formatted line with original left part + formatted_lines.append((left_part, right_part)) + + # Total width (max_left_length + 4) + total_width = max_left_length + 4 + + # Step 3: Create formatted output with proper spacing + output_lines = [] + for left_part, right_part in formatted_lines: + # Calculate lengths again for formatting + left_length_no_pipe = len(left_part.replace('|', '').strip()) + left_length_with_pipe = len(left_part.strip()) + + # Calculate number of spaces needed for padding after removing '|' + padding_spaces = total_width - left_length_no_pipe + + if left_part == "": + # If left_part is empty, pad right_part with underscores + right_part = '_' * total_width + right_part + + # Create a formatted line with calculated padding + formatted_line = left_part.ljust(len(left_part) + padding_spaces) + right_part + output_lines.append(formatted_line) + + return '\n'.join(output_lines) + + def get_formatted_help(self): + return self._format_columns() + def show(self, parent=None): if logger.isEnabledFor(logging.INFO): logger.info('>>> Message System: displaying key "{}"'.format(self._last_key)) diff --git a/pyradio/win.py b/pyradio/win.py index 506f5da..bb9590d 100644 --- a/pyradio/win.py +++ b/pyradio/win.py @@ -38,7 +38,7 @@ https://sourceforge.net/projects/mpv-player-windows/files/latest/download ''' zurl = [ - 'https://sourceforge.com/projects/mpv-player-windows/files/64bit/mpv-x86_64-20240825-git-cb4fdb5.7z/download', + 'https://sourceforge.com/projects/mpv-player-windows/files/64bit/mpv-x86_64-20241124-git-2d813de.7z/download', 'https://sourceforge.net/projects/mplayerwin/files/MPlayer-MEncoder/r38151/mplayer-svn-38151-x86_64.7z/download' ]