Skip to content
Merged
77 changes: 46 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>] [--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 <path> 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
<n>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.
<n>G Jump to n-th station.
Space Stop/start playing selected station.
Esc/q Quit.
```
12 changes: 6 additions & 6 deletions pyradio/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
69 changes: 62 additions & 7 deletions pyradio/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand All @@ -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()
Expand All @@ -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"""
Expand Down Expand Up @@ -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")
Expand All @@ -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")


Expand Down
6 changes: 4 additions & 2 deletions pyradio/radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down