mirror of
https://github.com/rembo10/headphones.git
synced 2026-07-20 16:03:59 +01:00
Added a music scanner to read metadata from audio files rather than using folder names/xml
This commit is contained in:
Executable
+223
@@ -0,0 +1,223 @@
|
||||
# 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.
|
||||
|
||||
"""Support for beets plugins."""
|
||||
|
||||
import logging
|
||||
import itertools
|
||||
import traceback
|
||||
from collections import defaultdict
|
||||
|
||||
PLUGIN_NAMESPACE = 'beetsplug'
|
||||
DEFAULT_PLUGINS = []
|
||||
|
||||
# Global logger.
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
|
||||
# Managing the plugins themselves.
|
||||
|
||||
class BeetsPlugin(object):
|
||||
"""The base class for all beets plugins. Plugins provide
|
||||
functionality by defining a subclass of BeetsPlugin and overriding
|
||||
the abstract methods defined here.
|
||||
"""
|
||||
def commands(self):
|
||||
"""Should return a list of beets.ui.Subcommand objects for
|
||||
commands that should be added to beets' CLI.
|
||||
"""
|
||||
return ()
|
||||
|
||||
def track_distance(self, item, info):
|
||||
"""Should return a (distance, distance_max) pair to be added
|
||||
to the distance value for every track comparison.
|
||||
"""
|
||||
return 0.0, 0.0
|
||||
|
||||
def album_distance(self, items, info):
|
||||
"""Should return a (distance, distance_max) pair to be added
|
||||
to the distance value for every album-level comparison.
|
||||
"""
|
||||
return 0.0, 0.0
|
||||
|
||||
def candidates(self, items):
|
||||
"""Should return a sequence of MusicBrainz info dictionaries
|
||||
that match the album whose items are provided.
|
||||
"""
|
||||
return ()
|
||||
|
||||
def item_candidates(self, item):
|
||||
"""Should return a sequence of MusicBrainz track info
|
||||
dictionaries that match the item provided.
|
||||
"""
|
||||
return ()
|
||||
|
||||
def configure(self, config):
|
||||
"""This method is called with the ConfigParser object after
|
||||
the CLI starts up.
|
||||
"""
|
||||
pass
|
||||
|
||||
listeners = None
|
||||
|
||||
@classmethod
|
||||
def register_listener(cls, event, func):
|
||||
"""Add a function as a listener for the specified event. (An
|
||||
imperative alternative to the @listen decorator.)
|
||||
"""
|
||||
if cls.listeners is None:
|
||||
cls.listeners = defaultdict(list)
|
||||
cls.listeners[event].append(func)
|
||||
|
||||
@classmethod
|
||||
def listen(cls, event):
|
||||
"""Decorator that adds a function as an event handler for the
|
||||
specified event (as a string). The parameters passed to function
|
||||
will vary depending on what event occurred.
|
||||
|
||||
The function should respond to named parameters.
|
||||
function(**kwargs) will trap all arguments in a dictionary.
|
||||
Example:
|
||||
|
||||
>>> @MyPlugin.listen("imported")
|
||||
>>> def importListener(**kwargs):
|
||||
>>> pass
|
||||
"""
|
||||
def helper(func):
|
||||
if cls.listeners is None:
|
||||
cls.listeners = defaultdict(list)
|
||||
cls.listeners[event].append(func)
|
||||
return func
|
||||
return helper
|
||||
|
||||
def load_plugins(names=()):
|
||||
"""Imports the modules for a sequence of plugin names. Each name
|
||||
must be the name of a Python module under the "beetsplug" namespace
|
||||
package in sys.path; the module indicated should contain the
|
||||
BeetsPlugin subclasses desired. A default set of plugins is also
|
||||
loaded.
|
||||
"""
|
||||
for name in itertools.chain(names, DEFAULT_PLUGINS):
|
||||
modname = '%s.%s' % (PLUGIN_NAMESPACE, name)
|
||||
try:
|
||||
try:
|
||||
__import__(modname, None, None)
|
||||
except ImportError, exc:
|
||||
# Again, this is hacky:
|
||||
if exc.args[0].endswith(' ' + name):
|
||||
log.warn('** plugin %s not found' % name)
|
||||
else:
|
||||
raise
|
||||
except:
|
||||
log.warn('** error loading plugin %s' % name)
|
||||
log.warn(traceback.format_exc())
|
||||
|
||||
_instances = {}
|
||||
def find_plugins():
|
||||
"""Returns a list of BeetsPlugin subclass instances from all
|
||||
currently loaded beets plugins. Loads the default plugin set
|
||||
first.
|
||||
"""
|
||||
load_plugins()
|
||||
plugins = []
|
||||
for cls in BeetsPlugin.__subclasses__():
|
||||
# Only instantiate each plugin class once.
|
||||
if cls not in _instances:
|
||||
_instances[cls] = cls()
|
||||
plugins.append(_instances[cls])
|
||||
return plugins
|
||||
|
||||
|
||||
# Communication with plugins.
|
||||
|
||||
def commands():
|
||||
"""Returns a list of Subcommand objects from all loaded plugins.
|
||||
"""
|
||||
out = []
|
||||
for plugin in find_plugins():
|
||||
out += plugin.commands()
|
||||
return out
|
||||
|
||||
def track_distance(item, info):
|
||||
"""Gets the track distance calculated by all loaded plugins.
|
||||
Returns a (distance, distance_max) pair.
|
||||
"""
|
||||
dist = 0.0
|
||||
dist_max = 0.0
|
||||
for plugin in find_plugins():
|
||||
d, dm = plugin.track_distance(item, info)
|
||||
dist += d
|
||||
dist_max += dm
|
||||
return dist, dist_max
|
||||
|
||||
def album_distance(items, info):
|
||||
"""Returns the album distance calculated by plugins."""
|
||||
dist = 0.0
|
||||
dist_max = 0.0
|
||||
for plugin in find_plugins():
|
||||
d, dm = plugin.album_distance(items, info)
|
||||
dist += d
|
||||
dist_max += dm
|
||||
return dist, dist_max
|
||||
|
||||
def candidates(items):
|
||||
"""Gets MusicBrainz candidates for an album from each plugin.
|
||||
"""
|
||||
out = []
|
||||
for plugin in find_plugins():
|
||||
out.extend(plugin.candidates(items))
|
||||
return out
|
||||
|
||||
def item_candidates(item):
|
||||
"""Gets MusicBrainz candidates for an item from the plugins.
|
||||
"""
|
||||
out = []
|
||||
for plugin in find_plugins():
|
||||
out.extend(plugin.item_candidates(item))
|
||||
return out
|
||||
|
||||
def configure(config):
|
||||
"""Sends the configuration object to each plugin."""
|
||||
for plugin in find_plugins():
|
||||
plugin.configure(config)
|
||||
|
||||
|
||||
# Event dispatch.
|
||||
|
||||
# All the handlers for the event system.
|
||||
# Each key of the dictionary should contain a list of functions to be
|
||||
# called for any event. Functions will be called in the order they were
|
||||
# added.
|
||||
_event_handlers = defaultdict(list)
|
||||
|
||||
def load_listeners():
|
||||
"""Loads and registers event handlers from all loaded plugins.
|
||||
"""
|
||||
for plugin in find_plugins():
|
||||
if plugin.listeners:
|
||||
for event, handlers in plugin.listeners.items():
|
||||
_event_handlers[event] += handlers
|
||||
|
||||
def send(event, **arguments):
|
||||
"""Sends an event to all assigned event listeners. Event is the
|
||||
name of the event to send, all other named arguments go to the
|
||||
event handler(s).
|
||||
|
||||
Returns the number of handlers called.
|
||||
"""
|
||||
log.debug('Sending event: %s' % event)
|
||||
handlers = _event_handlers[event]
|
||||
for handler in handlers:
|
||||
handler(**arguments)
|
||||
return len(handlers)
|
||||
Reference in New Issue
Block a user