diff --git a/NEWS b/NEWS index 1a26460c8..445e6cf61 100644 --- a/NEWS +++ b/NEWS @@ -7,13 +7,17 @@ plugin to work; documentation is forthcoming.) * To support the above, there's also a new system for extending the autotagger via plugins. Plugins can currently add components to the - track distance function. + track and album distance functions as well as augment the MusicBrainz + search. * New event system for plugins (thanks, Jeff!). Plugins can now get callbacks from beets when certain events occur in the core. * The BPD plugin is now disabled by default. This greatly simplifies installation of the beets core, which has no compiled dependencies now. To use BPD, though, you'll need to set "plugins: bpd" in your .beetsconfig. +* There's a new "mpdupdate" plugin that will automatically update your + MPD index whenever your beets library changes. Documentation on this + is forthcoming as well. * Fixed bug that completely broke non-autotagged imports ("import -A"). * Fixed bug that logged the wrong paths when using "import -l". * A new "-v" command line switch enables debugging output. diff --git a/beets/plugins.py b/beets/plugins.py index 718c335bb..8ed266fcc 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -57,6 +57,12 @@ class BeetsPlugin(object): """ return () + def configure(self, config): + """This method is called with the ConfigParser object after + the CLI starts up. + """ + pass + listeners = None @classmethod def listen(cls, event): @@ -117,7 +123,7 @@ def find_plugins(): return plugins -# Plugin commands. +# Communication with plugins. def commands(): """Returns a list of Subcommand objects from all loaded plugins. @@ -157,6 +163,11 @@ def candidates(items): out.extend(plugin.candidates(items)) return out +def configure(config): + """Sends the configuration object to each plugin.""" + for plugin in find_plugins(): + plugin.configure(config) + # Event dispatch. diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index da2f4427f..8209ac3e6 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -408,6 +408,7 @@ def main(): plugins.load_plugins(plugnames.split()) plugins.load_listeners() plugins.send("pluginload") + plugins.configure(config) # Construct the root parser. commands = list(default_commands) @@ -452,4 +453,3 @@ def main(): except UserError, exc: message = exc.args[0] if exc.args else None subcommand.parser.error(message) - diff --git a/beetsplug/mpdupdate.py b/beetsplug/mpdupdate.py new file mode 100644 index 000000000..7c4e5a8dd --- /dev/null +++ b/beetsplug/mpdupdate.py @@ -0,0 +1,106 @@ +# This file is part of beets. +# Copyright 2010, Adrian Sampson. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Updates an MPD index whenever the library is changed. + +Put something like the following in your .beetsconfig to configure: + [mpdupdate] + host = localhost + port = 6600 + password = seekrit +""" + +from beets.plugins import BeetsPlugin +from beets import ui +import socket + +# No need to introduce a dependency on an MPD library for such a +# simple use case. Here's a simple socket abstraction to make things +# easier. +class BufferedSocket(object): + """Socket abstraction that allows reading by line.""" + def __init__(self, sep='\n'): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.buf = '' + self.sep = sep + + def connect(self, host, port): + self.sock.connect((host, port)) + + def readline(self): + while self.sep not in self.buf: + data = self.sock.recv(1024) + if not data: + break + self.buf += data + if '\n' in self.buf: + res, self.buf = self.buf.split(self.sep, 1) + return res + self.sep + else: + return '' + + def send(self, data): + self.sock.send(data) + + def close(self): + self.sock.close() + +def update_mpd(host='localhost', port=6600, password=None): + """Sends the "update" command to the MPD server indicated, + possibly authenticating with a password first. + """ + print 'Updating MPD database...' + + s = BufferedSocket() + s.connect(host, port) + resp = s.readline() + if 'OK MPD' not in resp: + print 'MPD connection failed:', repr(resp) + return + + if password: + s.send('password "%s"\n' % password) + resp = s.readline() + if 'OK' not in resp: + print 'Authentication failed:', repr(resp) + s.send('close\n') + s.close() + return + + s.send('update\n') + resp = s.readline() + if 'updating_db' not in resp: + print 'Update failed:', repr(resp) + + s.send('close\n') + s.close() + print '... updated.' + +options = { + 'host': 'localhost', + 'port': 6600, + 'password': None, +} +class MPDUpdatePlugin(BeetsPlugin): + def configure(self, config): + options['host'] = \ + ui.config_val(config, 'mpdupdate', 'host', 'localhost') + options['port'] = \ + int(ui.config_val(config, 'mpdupdate', 'port', '6600')) + options['password'] = \ + ui.config_val(config, 'mpdupdate', 'password', '') + +@MPDUpdatePlugin.listen('save') +def update(lib=None): + update_mpd(options['host'], options['port'], options['password'])