diff --git a/Changelog b/Changelog index cb7237b6..92119801 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,9 @@ +2021-04-14 s-n-g + * Version 0.8.9.1 + * Implemented the so called "Listening" mode, in which PyRadio TUI + can be reduced down to a single line (the "Status Bar"). Requested + for tilling WM use (#128)[https://github.com/coderholic/pyradio/issues/128] + 2021-04-03 s-n-g * Version 0.8.9 * Implemented a simplified method to install, update, uninstall. diff --git a/README.html b/README.html index 3d793c75..1d9f4f4a 100644 --- a/README.html +++ b/README.html @@ -41,7 +41,7 @@
Note: When inserting numbers (either to jump to a station or to move a station), the number will be displayed at the right bottom corner of the window, suffixed by a “G”, i.e. pressing 35 will display [35G].
Note: When tagging a station position for a move action (by pressing “J”), the position will be displayed at the right bottom corner of the window, suffixed by a “J”, i.e. pressing “J” on position 35 will display [35J].
-PyRadio has the following primary modes:
A set of secondary modes is also available (a secondary mode works within a primary one):
If you are a packager and would like to produce a package for your distribution please do follow this mini guide.
-PyRadio is able to update and uninstall itself, when installed from source. This is something you do not want to be happening when your package is used; PyRadio should be updated and uninstalled using the distro package manager.
+First of all, make sure you declare the pacakges’s requirements to the relevant section of your manifest (or whatever) file. These are: 1. requests 2. dnspython
+After that, you will have to modify some files, because PyRadio is able to update and uninstall itself, when installed from source. This is something you do not want to be happening when your package is used; PyRadio should be updated and uninstalled using the distro package manager.
In order to accomplice that, you just have to change the distro configuration parameter in the config file. PyRadio will read this parameter and will disable updating and uninstalling, when set to anything other than “None”. So, here’s how you do that:
Once you are in the sources top level directory (typically “pyradio”), you execute the command:
sed -i 's/distro = None/distro = YOUR DISTRO NAME/' pyradio/configdiff --git a/README.md b/README.md index c009f418..ef37cc54 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Ben Dowling - [https://github.com/coderholic](https://github.com/coderholic) * [Installation](#installation) * [Command line options](#command-line-options) * [Controls](#controls) -* [PyRadio's Modes](#pyradio's-modes) +* [PyRadio Modes](#pyradio-modes) * [Config file](#config-file) * [About Playlist files](#about-playlist-files) * [Integrating new stations](#integrating-new-stations) @@ -57,6 +57,7 @@ Ben Dowling - [https://github.com/coderholic](https://github.com/coderholic) ## Requirements * python 2.7/3.5+ - requests + - dnspython * MPV, MPlayer or VLC installed and in your path ## Installation @@ -171,7 +172,7 @@ The same logic applies to all **PyRadio** windows. **Note:** When tagging a station position for a move action (by pressing "**J**"), the position will be displayed at the right bottom corner of the window, suffixed by a "*J*", i.e. pressing "*J*" on position *35* will display *[35J]*. -## PyRadio's Modes +## PyRadio Modes **PyRadio** has the following primary modes: @@ -183,6 +184,8 @@ The same logic applies to all **PyRadio** windows. 4. The **Register Main** mode, which is identical to the "*Main*" mode, except it displays the content of a **named** register. +5. The **Listening** mode, which is intended to be used when you want **PyRadio** to just play your favorite station and not take up too much space. It is ideal for tilling window manager use, as the whole TUI can be reduced all the way down to a single line (displaying the "*Status Bar*"). In this mode, adjusting, muting and saving the volume are the only action available. To get **PyRadio** back to normal operation one would just resize its window to a reasonable size (7 lines vertically, or more). + A set of **secondary modes** is also available (a secondary mode works within a primary one): 1. The **Extra Commands** mode, which gives you access to extra commands. You can enter this mode by pressing "**\\**" (backslash). Then a backslash is displayed at the bottom right corner of the window. @@ -730,7 +733,11 @@ Finally, include the file produced in your report. If you are a packager and would like to produce a package for your distribution please do follow this mini guide. -**PyRadio** is able to update and uninstall itself, when installed from source. This is something you do not want to be happening when your package is used; **PyRadio** should be updated and uninstalled using the distro package manager. +First of all, make sure you declare the pacakges's requirements to the relevant section of your manifest (or whatever) file. These are: +1. requests +2. dnspython + +After that, you will have to modify some files, because **PyRadio** is able to update and uninstall itself, when installed from source. This is something you do not want to be happening when your package is used; **PyRadio** should be updated and uninstalled using the distro package manager. In order to accomplice that, you just have to change the **distro** configuration parameter in the **config** file. **PyRadio** will read this parameter and will disable updating and uninstalling, when set to anything other than "**None**". So, here's how you do that: diff --git a/devel/build_install_pyradio.bat b/devel/build_install_pyradio.bat index 554fa950..e40b5f96 100644 --- a/devel/build_install_pyradio.bat +++ b/devel/build_install_pyradio.bat @@ -34,6 +34,7 @@ IF "%1"=="" ( pip install windows-curses --upgrade 1>NUL 2>NUL pip install pywin32 --upgrade 1>NUL 2>NUL pip install requests --upgrade 1>NUL 2>NUL + pip install dnspython --upgrade 1>NUL 2>NUL ) IF '%1'=='ELEV' ( GOTO START ) ELSE ( ECHO Running elevated in a different window) diff --git a/pyradio.1 b/pyradio.1 index f36ba943..5dde6a1c 100644 --- a/pyradio.1 +++ b/pyradio.1 @@ -158,6 +158,9 @@ The \fBRegisters\fR mode. This is identical to the "\fIPlaylist\fR" mode, but in .IP 4. 3 The \fBRegister Main\fR mode, which is identical to the "\fIMain\fR" mode, except it displays the content of a \fBnamed\fR register. +.IP 5. 3 +The \fBListening\fR mode, which is intended to be used when you want \fBpyradio\fR to just play your favorite station and not take up too much space. It is ideal for tilling window manager use, as the whole TUI can be reduced all the way down to a single line (displaying the "\fIStatus Bar\fR"). In this mode, adjusting, muting and saving the volume are the only actions available. To get \fBpyradio\fR back to normal operation one would just resize its window to a reasonable size (7 lines vertically, or more). + .RE A set of \fBsecondary modes\fR is also available (a secondary mode works within a primary one): diff --git a/pyradio/__init__.py b/pyradio/__init__.py index 2708d2f1..8e3fe19e 100644 --- a/pyradio/__init__.py +++ b/pyradio/__init__.py @@ -1,6 +1,6 @@ " pyradio -- Console radio player. " -version_info = (0, 8, 9) +version_info = (0, 8, 9, 1) # Application state: # New stable version: '' diff --git a/pyradio/browser.py b/pyradio/browser.py index cf26c947..455aab1d 100644 --- a/pyradio/browser.py +++ b/pyradio/browser.py @@ -215,7 +215,7 @@ def __init__(self, 'term': 'big band', 'param': {'order': 'votes', 'reverse': 'true'}, }) - self._search_history_index = 1 + self._search_history_index = 0 self.search() diff --git a/pyradio/install.py b/pyradio/install.py index 55a4630d..2c905665 100644 --- a/pyradio/install.py +++ b/pyradio/install.py @@ -173,11 +173,12 @@ def update_or_uninstall_on_windows(self, mode='update'): os.makedirs(self._dir, exist_ok=True) if mode.startswith('update'): bat = os.path.join(self._dir, 'update.bat') - PyRadioUpdateOnWindows.print_update_bat_created() + os.system('CLS') + # PyRadioUpdateOnWindows.print_update_bat_created() else: bat = os.path.join(self._dir, 'uninstall.bat') os.system('CLS') - PyRadioUpdateOnWindows.print_uninstall_bat_created() + # PyRadioUpdateOnWindows.print_uninstall_bat_created() try: with open(bat, "w") as b: b.write('@ECHO OFF\n') diff --git a/pyradio/radio.py b/pyradio/radio.py index 5201bb6b..1b4bcae5 100644 --- a/pyradio/radio.py +++ b/pyradio/radio.py @@ -522,60 +522,109 @@ def setup(self, stdscr): self.setupAndDrawScreen(init_from_setup=True) ''' position playlist in window ''' - self.outerBodyMaxY, self.outerBodyMaxX = self.outerBodyWin.getmaxyx() - self.bodyMaxY, self.bodyMaxX = self.bodyWin.getmaxyx() - if self.selections[self.ws.PLAYLIST_MODE][0] < self.bodyMaxY: + try: + self.outerBodyMaxY, self.outerBodyMaxX = self.outerBodyWin.getmaxyx() + self.bodyMaxY, self.bodyMaxX = self.bodyWin.getmaxyx() + except: + pass + try: + if self.selections[self.ws.PLAYLIST_MODE][0] < self.bodyMaxY: + self.selections[self.ws.PLAYLIST_MODE][1] = 0 + elif self.selections[self.ws.PLAYLIST_MODE][0] > len(self._cnf.playlists) - self.bodyMaxY + 1: + # TODO make sure this is ok + self.selections[self.ws.PLAYLIST_MODE][1] = len(self._cnf.playlists) - self.bodyMaxY + else: + self.selections[self.ws.PLAYLIST_MODE][1] = self.selections[self.ws.PLAYLIST_MODE][0] - int(self.bodyMaxY/2) + except: self.selections[self.ws.PLAYLIST_MODE][1] = 0 - elif self.selections[self.ws.PLAYLIST_MODE][0] > len(self._cnf.playlists) - self.bodyMaxY + 1: - # TODO make sure this is ok - self.selections[self.ws.PLAYLIST_MODE][1] = len(self._cnf.playlists) - self.bodyMaxY - else: - self.selections[self.ws.PLAYLIST_MODE][1] = self.selections[self.ws.PLAYLIST_MODE][0] - int(self.bodyMaxY/2) self.playlist_selections[self.ws.PLAYLIST_MODE] = self.selections[self.ws.PLAYLIST_MODE][:-1][:] # self.ll('setup') self.run() def setupAndDrawScreen(self, init_from_setup=False): + self._limited_height_mode = False self.maxY, self.maxX = self.stdscr.getmaxyx() self.headWin = None self.bodyWin = None self.outerBodyWin = None self.footerWin = None - self.headWin = curses.newwin(1, self.maxX, 0, 0) - self.outerBodyWin = curses.newwin(self.maxY - 2, self.maxX, 1, 0) - #self.bodyWin = curses.newwin(self.maxY - 2, self.maxX, 1, 0) - self.bodyWinStartY = 2 + self._cnf.internal_header_height - self.bodyWinEndY = self.maxY - self.bodyWinStartY - 1 - if logger.isEnabledFor(logging.DEBUG): - logger.debug('body starts at line {0}, ends at line {1}'.format(self.bodyWinStartY, self.bodyWinEndY)) - self.bodyWin = curses.newwin( - self.maxY - 4 - self._cnf.internal_header_height, - self.maxX - 2, - self.bodyWinStartY, - 1) self.footerWin = curses.newwin(1, self.maxX, self.maxY - 1, 0) - # txtWin used mainly for error reports - self.txtWin = None - self.txtWin = curses.newwin(self.maxY - 4, self.maxX - 4, 2, 2) - self.initHead(self._cnf.info) - self.initFooter() - self.log.setScreen(self.footerWin) - if init_from_setup: - if self.player: - self.log.write(msg='Selected player: ' + self.player.PLAYER_NAME, help_msg=True) - ''' for light color scheme ''' - # TODO - self.outerBodyWin.bkgdset(' ', curses.color_pair(5)) - self.bodyWin.bkgdset(' ', curses.color_pair(5)) - self.initBody() + self.headWin = curses.newwin(1, self.maxX, 0, 0) + if self.maxY < 8: + self._limited_height_mode = True + if self.maxY == 1: + self.bodyWin = self.footerWin + else: + self.bodyWin = curses.newwin( + self.maxY - 1, self.maxX, 0, 0) + self.bodyWin.bkgdset(' ', curses.color_pair(5)) + self.bodyWin.erase() + if self.player.isPlaying(): + self.bodyWin.addstr(self.maxY - 2, 0, ' Station: ', curses.color_pair(5)) + try: + self.bodyWin.addstr(self._last_played_station[0], curses.color_pair(4)) + except: + pass + else: + self.bodyWin.addstr(self.maxY - 2, 0, ' Status: ', curses.color_pair(5)) + self.bodyWin.addstr('Idle', curses.color_pair(4)) + if self.maxY - 3 >= 0: + if self._cnf.browsing_station_service: + self.bodyWin.addstr(self.maxY - 3, 0, ' Service: ', curses.color_pair(5)) + else: + self.bodyWin.addstr(self.maxY - 3, 0, ' Playlist: ', curses.color_pair(5)) + try: + self.bodyWin.addstr(self._cnf.station_title, curses.color_pair(4)) + except: + pass + if self.maxY - 4 >= 0: + self._cnf.get_pyradio_version(), + self.bodyWin.addstr(self.maxY - 4, 0, 'PyRadio ' + self._cnf.current_pyradio_version, curses.color_pair(4)) + + self.bodyMaxY, self.bodyMaxX = self.bodyWin.getmaxyx() + self.bodyWin.refresh() + + else: + self.outerBodyWin = curses.newwin(self.maxY - 2, self.maxX, 1, 0) + #self.bodyWin = curses.newwin(self.maxY - 2, self.maxX, 1, 0) + self.bodyWinStartY = 2 + self._cnf.internal_header_height + self.bodyWinEndY = self.maxY - self.bodyWinStartY - 1 + if logger.isEnabledFor(logging.DEBUG): + logger.debug('body starts at line {0}, ends at line {1}'.format(self.bodyWinStartY, self.bodyWinEndY)) + self.bodyWin = curses.newwin( + self.maxY - 4 - self._cnf.internal_header_height, + self.maxX - 2, + self.bodyWinStartY, + 1) + + # txtWin used mainly for error reports + self.txtWin = None + try: + self.txtWin = curses.newwin(self.maxY - 4, self.maxX - 4, 2, 2) + except: + pass + if not self._limited_height_mode: + self.initHead(self._cnf.info) + ''' for light color scheme ''' + # TODO + self.outerBodyWin.bkgdset(' ', curses.color_pair(5)) + self.bodyWin.bkgdset(' ', curses.color_pair(5)) + self.initBody() #self.stdscr.timeout(100) self.bodyWin.keypad(1) #self.stdscr.noutrefresh() + self.initFooter() + self.log.setScreen(self.footerWin) + if init_from_setup: + if self.player: + self.log.write(msg='Selected player: ' + self.player.PLAYER_NAME, help_msg=True) + else: + self.footerWin.refresh() curses.doupdate() def initHead(self, info): @@ -647,6 +696,13 @@ def initBody(self): def initFooter(self): ''' Initializes the body/story window ''' + + ''' This would be the first step to make the status bar + appear as plain text in "Listening Mode" + + col = 5 if self._limited_height_mode else 7 + self.footerWin.bkgd(' ', curses.color_pair(col)) + ''' self.footerWin.bkgd(' ', curses.color_pair(7)) self.footerWin.noutrefresh() @@ -3929,6 +3985,23 @@ def _normal_station_info(self): else: self._print_station_info_error() + def _handle_limited_height_keys(self, char): + if char in (ord('+'), ord('='), ord('.')): + self._update_status_bar_right() + self._volume_up() + + elif char in (ord('-'), ord(',')): + self._update_status_bar_right() + self._volume_down() + + elif char in (ord('m'), ): + self._update_status_bar_right() + self._volume_mute() + + elif char in (ord('v'), ): + self._update_status_bar_right() + self._volume_save() + def keypress(self, char): self.detect_if_player_exited = True if self._system_asked_to_terminate: @@ -3942,7 +4015,11 @@ def keypress(self, char): self._do_display_notify() return - elif self.ws.operation_mode in ( + if self._limited_height_mode: + self._handle_limited_height_keys(char) + return + + if self.ws.operation_mode in ( self.ws.NO_PLAYER_ERROR_MODE, self.ws.CONFIG_SAVE_ERROR_MODE ): @@ -4852,9 +4929,10 @@ def keypress(self, char): elif char in (ord('/'), ) and self.ws.operation_mode in self._search_modes.keys(): self._update_status_bar_right() - self._give_me_a_search_class(self.ws.operation_mode) - self.search.show(self.outerBodyWin) - self.ws.operation_mode = self._search_modes[self.ws.operation_mode] + if self.maxY > 5: + self._give_me_a_search_class(self.ws.operation_mode) + self.search.show(self.outerBodyWin) + self.ws.operation_mode = self._search_modes[self.ws.operation_mode] return @@ -5016,24 +5094,10 @@ def keypress(self, char): self._toggle_transparency() return - elif char in (ord('+'), ord('='), ord('.')): - self._update_status_bar_right() - self._volume_up() - return - - elif char in (ord('-'), ord(',')): - self._update_status_bar_right() - self._volume_down() - return - - elif char in (ord('m'), ): - self._update_status_bar_right() - self._volume_mute() - return - - elif char in (ord('v'), ): - self._update_status_bar_right() - self._volume_save() + elif char in (ord('+'), ord('='), ord('.'), + ord('-'), ord(','), ord('m'), + ord('v')): + self._handle_limited_height_keys(char) return elif self.ws.operation_mode == self.ws.PLAYLIST_SCAN_ERROR_MODE: @@ -6193,9 +6257,12 @@ def _find_renamed_selection(self, mode, search_path, search_file): def _redisplay_stations_and_playlists(self): + if self._limited_height_mode: + return self.bodyWin.erase() self.outerBodyWin.erase() - self.outerBodyWin.box() + if self.maxY > 2: + self.outerBodyWin.box() try: self.bodyWin.move(1, 1) self.bodyWin.move(0, 0)