diff --git a/docs/source/moc.rst b/docs/source/moc.rst index 1400ea0..6b3cd74 100644 --- a/docs/source/moc.rst +++ b/docs/source/moc.rst @@ -22,6 +22,8 @@ Controlling the playback .. autofunction:: increase_volume .. autofunction:: decrease_volume +.. autofunction:: get_volume +.. autofunction:: set_volume .. autofunction:: moc.next .. autofunction:: moc.previous diff --git a/moc/__init__.py b/moc/__init__.py index b30c1b5..903d447 100644 --- a/moc/__init__.py +++ b/moc/__init__.py @@ -1,7 +1,16 @@ #!/usr/bin/env python from __future__ import with_statement -import os +import os, os.path import subprocess +import socket +import struct +import ConfigParser +import StringIO + +class Cli: + extra_arguments = [] + configfile = "~/.moc/config" + socketfile = "~/.moc/socket2" STATE_NOT_RUNNING = -1 STATE_STOPPED = 0 @@ -21,24 +30,27 @@ class MocNotRunning(MocError): """ Raised if a command failed because the moc server does not run """ # Helper functions -def _quote_file_args(files): +def _check_file_args(files): + """ + Checks if every element from dictonary passed in parameter is a valid + filepath, is a http or ftp url. + + Raises TypeError if parameter is a string or OSError if any of files + in dictonary does not exist. + """ if isinstance(files, str): raise TypeError("Argument must be a list/iterable, not str") - quoted = [] for file in files: - if os.path.exists(file) or file.startswith(('http://', 'ftp://')): + if not os.path.exists(file) and not file.startswith(('http://', 'ftp://')): # MOC only supports HTTP and FTP, not even HTTPS. # (See `is_url` in `files.c`.) - quoted.append('"%s"' % file) - else: raise OSError("File %r does not exist" % file) - return ' '.join(quoted) -def _exec_command(command, parameters=''): +def _exec_command(command, parameters=[]): cmd = subprocess.Popen( - ['mocp --%s %s' %(command, parameters)], + ["mocp", "--%s" % command] + parameters + Cli.extra_arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - shell=True, close_fds=True + close_fds=True ) stdout, stderr = cmd.communicate() if cmd.returncode: @@ -49,9 +61,33 @@ def _exec_command(command, parameters=''): raise MocError(errmsg) return stdout +def set_config_file(config_file_path): + if not os.path.exists(config_file_path): + raise OSError("Configuration file '%r' does not exists" % config_file_path) + + Cli.configfile = config_file_path + Cli.extra_arguments = Cli.extra_arguments + ["--config", config_file_path] + update_moc_dir() + +def update_moc_dir(): + """ + Reads configuration file and searches for mocdir + """ + + configF = StringIO.StringIO() + configF.write('[dummysection]') + configF.write(open(os.path.expanduser(Cli.configfile), 'r').read()) + configF.seek(0, os.SEEK_SET) + + config = ConfigParser.RawConfigParser() + config.readfp(configF) + if config.get('dummysection', 'MOCDir'): + Cli.socketfile = config.get('dummysection','MOCDir') + '/socket2' + def start_server(): """ Starts the moc server. """ _exec_command('server') + update_moc_dir() def stop_server(): """ Shuts down the moc server. """ @@ -124,7 +160,8 @@ def quickplay(files): Raises an :exc:`OSError` if any of the `files` can not be found. """ - _exec_command('playit', _quote_file_args(files)) + _check_file_args(files) + _exec_command('playit', files) def _moc_output_to_dict(output): @@ -183,25 +220,38 @@ def increase_volume(level=5): """ Aliases: ``increase_volume()``, ``volume_up()``, ``louder()``, ``upper_volume()`` """ - _exec_command('volume', '+%d' % level) + _exec_command('volume', ['+%d' % level]) louder = upper_volume = volume_up = increase_volume def decrease_volume(level=5): """ Aliases: ``decrease_volume()``, ``volume_down()``, ``lower()``, ``lower_volume()`` """ - _exec_command('volume', '-%d' % level) + _exec_command('volume', ['-%d' % level]) lower = lower_volume = volume_down = decrease_volume +def get_volume(): + s = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM ) + s.connect(os.path.expanduser(Cli.socketfile)) + s.send(struct.pack('i', 0x1a)) + unpacker = struct.Struct('i i') + data = s.recv(unpacker.size) + s.close() + + return unpacker.unpack(data)[1] + +def set_volume(level): + _exec_command('volume', ['%d' % level]) + def seek(n): """ Moves the current playback seed forward by `n` seconds (or backward if `n` is negative). """ - _exec_command('seek', n) + _exec_command('seek', [n]) def _controls(what): - makefunc = lambda action: lambda: _exec_command(action, what) and None or None + makefunc = lambda action: lambda: _exec_command(action, [what]) and None or None return (makefunc(action) for action in ('on', 'off', 'toggle')) enable_repeat, disable_repeat, toggle_repeat = _controls('repeat') @@ -260,7 +310,8 @@ def playlist_append(files_directories_playlists): Appends the files, directories and/or in `files_directories_playlists` to moc's playlist. """ - _exec_command('append', _quote_file_args(files_directories_playlists)) + _check_file_args(files_directories_playlists) + _exec_command('append', files_directories_playlists) append_to_playlist = playlist_append def playlist_clear():