diff --git a/README.md b/README.md index daa674d7..acccc63e 100644 --- a/README.md +++ b/README.md @@ -4,61 +4,76 @@ Command line internet radio player. Ben Dowling - [http://www.coderholic.com/pyradio](http://www.coderholic.com/pyradio) - ## Requirements * python 2.6+/3.2+ -* mplayer or vlc installed and in your path +* python-pip +* mpv, mplayer or vlc installed and in your path. +* socat (if you wish to use MPV) +## Installation via pip -## Installation +This is the simplist method for installing PyRadio. -The first thing is to make sure MPlayer and VLC are installed and are in the +The first thing is to make sure MPV, MPlayer or VLC are installed and are in the PATH. To check this, go in your favorite terminal and make sure thiese programs -are launched when you type "mplayer" or "cvlc". +are launched when you type "mpv", "mplayer" or "cvlc". + +#### MacOSX + +MPlayer is one of the available packages provided by [Homebrew](https://github.com/Homebrew/homebrew). -MacOSX tip: MPlayer is one of the available packages provided by -[Homebrew](https://github.com/Homebrew/homebrew). But it is not the case of -VLC. Nevertheless, pyradio will also work with the binary of the application -[VLC](http://www.videolan.org/vlc/download-macosx.html). You simply can add a -symbolic link to the executable as follows (this link must of course be in your -PATH): +But it is not the case of VLC. Nevertheless, pyradio will also work with the binary of the application [VLC](http://www.videolan.org/vlc/download-macosx.html). + +You simply can add a symbolic link to the executable as follows (this link must of course be in your PATH): ln -s /Applications/VLC.app/Contents/MacOS/VLC cvlc -The second step is to install the python package: +The second step is to use pip to install the python package: pip install pyradio +#### Linux + + pip install pyradio + +## Building from source -## Shell +#### MaxOSX and Linux + + python setup.py build + +The second step is to install the build: + + sudo python setup.py install + +## Shell commands $ pyradio -h - usage: main.py [-h] [--stations STATIONS] [--random] [--add] [--list] + usage: main.py [--help] [--stations ] [--random] [--add] [--list] Console radio player optional arguments: - -h, --help show this help message and exit - --stations STATIONS, -s STATIONS - Path on stations csv file. - --random, -r Start and play random station. - --add, -a Add station to list. - --list, -l List of added stations. - --debug, -d Debug mode (pyradio.log created) + -h, --help Show this help message and exit. + -s, --stations Use specified station CSV file. + -r, --random Start and play a random station. + -a, --add Add station to list. + -l, --list List of added stations. + -d, --debug Debug mode (pyradio.log created). To be attached with any bug report. - ## Controls + ``` -Up/Down/j/k/PgUp/PgDown Change station selection -Enter Play selected station --/+ Change volume -m Mute -r Select and play a random station -g Jump to first station -G Jump to n-th station -Space Stop/start playing selected station -Esc/q Quit +Up/Down/j/k/PgUp/PgDown Change station selection. +Enter Play selected station. +-/+ Change volume. +m Mute. +r Select and play a random station. +g Jump to first station. +G Jump to n-th station. +Space Stop/start playing selected station. +Esc/q Quit. ``` diff --git a/pyradio/main.py b/pyradio/main.py index 03a51ac5..f6d2ab6e 100644 --- a/pyradio/main.py +++ b/pyradio/main.py @@ -38,16 +38,16 @@ def __configureLogger(): def shell(): parser = ArgumentParser(description="Console radio player") - parser.add_argument("--stations", "-s", default=DEFAULT_FILE, - help="Path on stations csv file.") + parser.add_argument("-s", "--stations", default=DEFAULT_FILE, + help="Use specified station CSV file.") parser.add_argument("--play", "-p", nargs='?', default=False, - help="Start and play. " + help="Start and play." "The value is num station or empty for random.") - parser.add_argument("--add", "-a", action='store_true', + parser.add_argument("-a", "--add", action='store_true', help="Add station to list.") - parser.add_argument("--list", "-l", action='store_true', + parser.add_argument("-l", "--list", action='store_true', help="List of added stations.") - parser.add_argument("--debug", "-d", action='store_true', + parser.add_argument("-d", "--debug", action='store_true', help="Start pyradio in debug mode.") args = parser.parse_args() diff --git a/pyradio/player.py b/pyradio/player.py index dd50b9ef..1f57bbc3 100644 --- a/pyradio/player.py +++ b/pyradio/player.py @@ -7,7 +7,7 @@ class Player(object): - """ Media player class. Playing is handled by mplayer """ + """ Media player class. Playing is handled by player sub classes """ process = None def __init__(self, outputStream): @@ -55,7 +55,7 @@ def play(self, streamUrl): logger.debug("Player started") def _sendCommand(self, command): - """ send keystroke command to mplayer """ + """ send keystroke command to player """ if(self.process is not None): try: @@ -68,7 +68,7 @@ def _sendCommand(self, command): exc_info=True) def close(self): - """ exit pyradio (and kill mplayer instance) """ + """ exit pyradio (and kill player instance) """ # First close the subprocess self._stop() @@ -94,6 +94,61 @@ def volumeUp(self): def volumeDown(self): pass +class MpvPlayer(Player): + """Implementation of Player object for MPV""" + + PLAYER_CMD = "mpv" + + os.system("rm /tmp/mpvsocket"); + + def _buildStartOpts(self, streamUrl, playList=False): + """ Builds the options to pass to subprocess.""" + + """ Test for newer MPV versions as it supports different IPC flags. """ + p = subprocess.Popen([self.PLAYER_CMD, "--input-ipc-server"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=False) + out = p.communicate() + if "not found" not in out[0]: + if logger.isEnabledFor(logging.DEBUG): + logger.debug("--input-ipc-server is supported.") + newerMpv = 1; + else: + if logger.isEnabledFor(logging.DEBUG): + logger.debug("--input-ipc-server is not supported.") + newerMpv = 0; + + if playList: + if newerMpv: + opts = [self.PLAYER_CMD, "--quiet", "--playlist", streamUrl, "--input-ipc-server=/tmp/mpvsocket"] + else: + opts = [self.PLAYER_CMD, "--quiet", "--playlist", streamUrl, "--input-unix-socket=/tmp/mpvsocket"] + else: + if newerMpv: + opts = [self.PLAYER_CMD, "--quiet", streamUrl, "--input-ipc-server=/tmp/mpvsocket"] + else: + opts = [self.PLAYER_CMD, "--quiet", streamUrl, "--input-unix-socket=/tmp/mpvsocket"] + return opts + + def mute(self): + """ mute mpv """ + os.system("echo 'cycle mute' | socat - /tmp/mpvsocket"); + + def pause(self): + """ pause streaming (if possible) """ + os.system("echo 'cycle pause' | socat - /tmp/mpvsocket"); + + def _stop(self): + """ exit pyradio (and kill mpv instance) """ + os.system("echo 'quit' | socat - /tmp/mpvsocket"); + os.system("rm /tmp/mpvsocket"); + + def volumeUp(self): + """ increase mpv's volume """ + os.system("echo 'cycle volume' | socat - /tmp/mpvsocket"); + + def volumeDown(self): + """ decrease mpv's volume """ + os.system("echo 'cycle volume down' | socat - /tmp/mpvsocket"); + class MpPlayer(Player): """Implementation of Player object for MPlayer""" @@ -142,7 +197,7 @@ def _buildStartOpts(self, streamUrl, playList=False): return opts def mute(self): - """ mute mplayer """ + """ mute vlc """ if not self.muted: self._sendCommand("volume 0\n") @@ -156,15 +211,15 @@ def pause(self): self._sendCommand("stop\n") def _stop(self): - """ exit pyradio (and kill mplayer instance) """ + """ exit pyradio (and kill vlc instance) """ self._sendCommand("shutdown\n") def volumeUp(self): - """ increase mplayer's volume """ + """ increase vlc's volume """ self._sendCommand("volup\n") def volumeDown(self): - """ decrease mplayer's volume """ + """ decrease vlc's volume """ self._sendCommand("voldown\n") diff --git a/pyradio/radio.py b/pyradio/radio.py index d411a6b6..3f9646e9 100644 --- a/pyradio/radio.py +++ b/pyradio/radio.py @@ -4,6 +4,8 @@ # http://www.coderholic.com/pyradio # Ben Dowling - 2009 - 2010 # Kirill Klenov - 2012 +# Peter Stevenson (2E0PGS) - 2018 + import curses import logging import os @@ -55,7 +57,7 @@ def setup(self, stdscr): curses.init_pair(9, curses.COLOR_BLACK, curses.COLOR_GREEN) self.log = Log() - # For the time being, supported players are mplayer and vlc. + # For the time being, supported players are mpv, mplayer and vlc. self.player = player.probePlayer()(self.log) self.stdscr.nodelay(0) @@ -180,7 +182,7 @@ def playSelection(self): self.player.play(stream_url) except OSError: self.log.write('Error starting player.' - 'Are you sure mplayer is installed?') + 'Are you sure a supported player is installed?') def keypress(self, char): # Number of stations to change with the page up/down keys