begin Confit-ifying plugins in alphabetical order

This commit is contained in:
Adrian Sampson 2012-12-13 12:31:10 -08:00
parent e17cd6beba
commit 6c94358b13
11 changed files with 112 additions and 106 deletions

View file

@ -471,7 +471,7 @@ def tag_item(item, search_artist=None, search_title=None,
hooks.TrackMatch(dist, track_info)
# If this is a good match, then don't keep searching.
rec = recommendation(candidates.values())
if rec == RECOMMEND_STRONG and not config['import_timid'].get(bool):
if rec == RECOMMEND_STRONG and not config['import']['timid']:
log.debug('Track ID match.')
return candidates.values(), rec

View file

@ -628,7 +628,7 @@ def initial_lookup(session):
if task.sentinel:
continue
plugins.send('import_task_start', task=task)
plugins.send('import_task_start', session=session, task=task)
log.debug('Looking up: %s' % task.path)
try:
@ -654,7 +654,7 @@ def user_query(session):
choice = session.choose_match(task)
task.set_choice(choice)
session.log_choice(task)
plugins.send('import_task_choice', task=task)
plugins.send('import_task_choice', session=session, task=task)
# As-tracks: transition to singleton workflow.
if choice is action.TRACKS:
@ -726,7 +726,7 @@ def apply_choices(session):
)
else:
autotag.apply_item_metadata(task.item, task.match.info)
plugins.send('import_task_apply', task=task)
plugins.send('import_task_apply', session=session, task=task)
# Infer album-level fields.
if task.is_album:
@ -852,7 +852,7 @@ def manipulate_files(session):
session.lib.store(item)
# Plugin event.
plugins.send('import_task_files', task=task)
plugins.send('import_task_files', session=session, task=task)
def finalize(session):
"""A coroutine that finishes up importer tasks. In particular, the
@ -908,7 +908,7 @@ def item_lookup(session):
if task.sentinel:
continue
plugins.send('import_task_start', task=task)
plugins.send('import_task_start', session=session, task=task)
task.set_item_candidates(*autotag.tag_item(task.item))
@ -926,7 +926,7 @@ def item_query(session):
choice = session.choose_item(task)
task.set_choice(choice)
session.log_choice(task)
plugins.send('import_task_choice', task=task)
plugins.send('import_task_choice', session=session, task=task)
# Duplicate check.
if task.choice_flag in (action.ASIS, action.APPLY):

View file

@ -56,7 +56,7 @@ class BenchmarkPlugin(BeetsPlugin):
"""A plugin for performing some simple performance benchmarks.
"""
def commands(self):
def bench_func(lib, config, opts, args):
def bench_func(lib, opts, args):
benchmark(lib, opts.profile)
bench_cmd = ui.Subcommand('bench', help='benchmark')
bench_cmd.parser.add_option('-p', '--profile',

View file

@ -29,12 +29,17 @@ import beets
from beets.plugins import BeetsPlugin
import beets.ui
from beets import vfs
from beets import config
from beets.util import bluelet
config.add({
'bpd': {
'host': u'',
'port': 6600,
'password': u'',
}
})
DEFAULT_PORT = 6600
DEFAULT_HOST = ''
DEFAULT_PASSWORD = ''
PROTOCOL_VERSION = '0.13.0'
BUFSIZE = 1024
@ -154,8 +159,7 @@ class BaseServer(object):
This is a generic superclass and doesn't support many commands.
"""
def __init__(self, host=DEFAULT_HOST, port=DEFAULT_PORT,
password=DEFAULT_PASSWORD):
def __init__(self, host, port, password):
"""Create a new server bound to address `host` and listening
on port `port`. If `password` is given, it is required to do
anything significant on the server.
@ -727,7 +731,7 @@ class Server(BaseServer):
to store its library.
"""
def __init__(self, library, host='', port=DEFAULT_PORT, password=''):
def __init__(self, library, host, port, password):
try:
from beetsplug.bpd import gstplayer
except ImportError as e:
@ -1144,15 +1148,12 @@ class BPDPlugin(BeetsPlugin):
cmd.parser.add_option('-d', '--debug', action='store_true',
help='dump all MPD traffic to stdout')
def func(lib, config, opts, args):
host = args.pop(0) if args else \
beets.ui.config_val(config, 'bpd', 'host', DEFAULT_HOST)
port = args.pop(0) if args else \
beets.ui.config_val(config, 'bpd', 'port', str(DEFAULT_PORT))
def func(lib, opts, args):
host = args.pop(0) if args else config['bpd']['host'].get(unicode)
port = args.pop(0) if args else config['bpd']['port'].get(int)
if args:
raise beets.ui.UserError('too many arguments')
password = beets.ui.config_val(config, 'bpd', 'password',
DEFAULT_PASSWORD)
password = config['bpd']['password'].get(unicode)
debug = opts.debug or False
self.start_bpd(lib, host, int(port), password, debug)

View file

@ -18,6 +18,8 @@ autotagger. Requires the pyacoustid library.
from beets import plugins
from beets import ui
from beets import util
from beets import config
from beets.util import confit
from beets.autotag import hooks
import acoustid
import logging
@ -43,9 +45,6 @@ _matches = {}
_fingerprints = {}
_acoustids = {}
# The user's Acoustid API key, if provided.
_userkey = None
def acoustid_match(path):
"""Gets metadata for a file from Acoustid and populates the
@ -148,17 +147,15 @@ class AcoustidPlugin(plugins.BeetsPlugin):
log.debug('acoustid item candidates: {0}'.format(len(tracks)))
return tracks
def configure(self, config):
global _userkey
_userkey = ui.config_val(config, 'acoustid', 'apikey', None)
def commands(self):
submit_cmd = ui.Subcommand('submit',
help='submit Acoustid fingerprints')
def submit_cmd_func(lib, config, opts, args):
if not _userkey:
def submit_cmd_func(lib, opts, args):
try:
apikey = config['acoustid']['apikey'].get(unicode)
except confit.NotFoundError:
raise ui.UserError('no Acoustid user API key provided')
submit_items(_userkey, lib.items(ui.decargs(args)))
submit_items(apikey, lib.items(ui.decargs(args)))
submit_cmd.func = submit_cmd_func
return [submit_cmd]
@ -166,7 +163,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
# Hooks into import process.
@AcoustidPlugin.listen('import_task_start')
def fingerprint_task(config=None, task=None):
def fingerprint_task(task):
"""Fingerprint each item in the task for later use during the
autotagging candidate search.
"""
@ -175,7 +172,7 @@ def fingerprint_task(config=None, task=None):
acoustid_match(item.path)
@AcoustidPlugin.listen('import_task_apply')
def apply_acoustid_metadata(config=None, task=None):
def apply_acoustid_metadata(task):
"""Apply Acoustid metadata (fingerprint and ID) to the task's items.
"""
for item in task.imported_items():

View file

@ -16,25 +16,37 @@
"""
import logging
import os
import shutil
import threading
from subprocess import Popen, PIPE
from subprocess import Popen
from beets.plugins import BeetsPlugin
from beets import ui, util
from beetsplug.embedart import _embed
from beets import config
log = logging.getLogger('beets')
DEVNULL = open(os.devnull, 'wb')
conf = {}
_fs_lock = threading.Lock()
config.add({
'convert': {
u'dest': None,
u'threads': util.cpu_count(),
u'ffmpeg': u'ffmpeg',
u'opts': u'-aq 2',
u'max_bitrate': 500,
u'embed': True,
}
})
def encode(source, dest):
log.info(u'Started encoding {0}'.format(util.displayable_path(source)))
encode = Popen([conf['ffmpeg']] + ['-i', source] + conf['opts'] +
[dest], close_fds=True, stderr=DEVNULL)
opts = config['convert']['opts'].get(unicode).split(u' ')
encode = Popen([config['convert']['ffmpeg'].get(unicode), '-i', source] +
opts + [dest],
close_fds=True, stderr=DEVNULL)
encode.wait()
if encode.returncode != 0:
# Something went wrong (probably Ctrl+C), remove temporary files
@ -64,7 +76,8 @@ def convert_item(lib, dest_dir):
with _fs_lock:
util.mkdirall(dest)
if item.format == 'MP3' and item.bitrate < 1000 * conf['max_bitrate']:
maxbr = config['convert']['max_bitrate'].get(int)
if item.format == 'MP3' and item.bitrate < 1000 * maxbr:
log.info(u'Copying {0}'.format(util.displayable_path(item.path)))
util.copy(item.path, dest)
else:
@ -74,17 +87,19 @@ def convert_item(lib, dest_dir):
item.write()
artpath = lib.get_album(item).artpath
if artpath and conf['embed']:
if artpath and config['convert']['embed']:
_embed(artpath, [item])
def convert_func(lib, config, opts, args):
dest = opts.dest if opts.dest is not None else conf['dest']
def convert_func(lib, opts, args):
dest = opts.dest if opts.dest is not None else \
config['convert']['dest'].get()
if not dest:
raise ui.UserError('no convert destination set')
threads = opts.threads if opts.threads is not None else conf['threads']
threads = opts.threads if opts.threads is not None else \
config['convert']['threads'].get(int)
ui.commands.list_items(lib, ui.decargs(args), opts.album, None, config)
ui.commands.list_items(lib, ui.decargs(args), opts.album, None)
if not ui.input_yn("Convert? (Y/n)"):
return
@ -99,18 +114,6 @@ def convert_func(lib, config, opts, args):
class ConvertPlugin(BeetsPlugin):
def configure(self, config):
conf['dest'] = ui.config_val(config, 'convert', 'dest', None)
conf['threads'] = int(ui.config_val(config, 'convert', 'threads',
util.cpu_count()))
conf['ffmpeg'] = ui.config_val(config, 'convert', 'ffmpeg', 'ffmpeg')
conf['opts'] = ui.config_val(config, 'convert',
'opts', '-aq 2').split(' ')
conf['max_bitrate'] = int(ui.config_val(config, 'convert',
'max_bitrate', '500'))
conf['embed'] = ui.config_val(config, 'convert', 'embed', True,
vtype=bool)
def commands(self):
cmd = ui.Subcommand('convert', help='convert to external location')
cmd.parser.add_option('-a', '--album', action='store_true',

View file

@ -19,15 +19,19 @@ import logging
from beets.plugins import BeetsPlugin
from beets import ui
from beets.ui import commands
from beets import config
import pyechonest.config
import pyechonest.song
# Global logger.
log = logging.getLogger('beets')
# The official Echo Nest API key for beets. This can be overridden by
# the user.
ECHONEST_APIKEY = 'NY2KTZHQ0QDSHBAP6'
config.add({
'echonest_tempo': {
'apikey': u'NY2KTZHQ0QDSHBAP6',
'auto': True,
}
})
def fetch_item_tempo(lib, loglevel, item, write):
"""Fetch and store tempo for a single item. If ``write``, then the
@ -68,22 +72,23 @@ def get_tempo(artist, title):
else:
return None
AUTOFETCH = True
class EchoNestTempoPlugin(BeetsPlugin):
def __init__(self):
super(EchoNestTempoPlugin, self).__init__()
self.import_stages = [self.imported]
pyechonest.config.ECHO_NEST_API_KEY = \
config['echonest_tempo']['apikey'].get(unicode)
def commands(self):
cmd = ui.Subcommand('tempo', help='fetch song tempo (bpm)')
cmd.parser.add_option('-p', '--print', dest='printbpm',
action='store_true', default=False,
help='print tempo (bpm) to console')
def func(lib, config, opts, args):
def func(lib, opts, args):
# The "write to files" option corresponds to the
# import_write config value.
write = ui.config_val(config, 'beets', 'import_write',
commands.DEFAULT_IMPORT_WRITE, bool)
write = config['import']['write'].get(bool)
for item in lib.items(ui.decargs(args)):
fetch_item_tempo(lib, logging.INFO, item, write)
@ -92,16 +97,8 @@ class EchoNestTempoPlugin(BeetsPlugin):
cmd.func = func
return [cmd]
def configure(self, config):
global AUTOFETCH
AUTOFETCH = ui.config_val(config, 'echonest_tempo', 'autofetch', True,
bool)
apikey = ui.config_val(config, 'echonest_tempo', 'apikey',
ECHONEST_APIKEY)
pyechonest.config.ECHO_NEST_API_KEY = apikey
# Auto-fetch tempo on import.
def imported(self, config, task):
if AUTOFETCH:
if config['echonest_tempo']['auto']:
for item in task.imported_items():
fetch_item_tempo(config.lib, logging.DEBUG, item, False)

44
beetsplug/embedart.py Executable file → Normal file
View file

@ -22,14 +22,23 @@ from beets import ui
from beets.ui import decargs
from beets.util import syspath, normpath
from beets.util.artresizer import ArtResizer
from beets import config
log = logging.getLogger('beets')
config.add({
'embedart': {
'maxwidth': 0,
'auto': True,
}
})
def _embed(path, items):
"""Embed an image file, located at `path`, into each item.
"""
if options['maxwidth']:
path = ArtResizer.shared.resize(options['maxwidth'], syspath(path))
maxwidth = config['embedart']['maxwidth'].get(int)
if maxwidth:
path = ArtResizer.shared.resize(maxwidth, syspath(path))
data = open(syspath(path), 'rb').read()
kindstr = imghdr.what(None, data)
@ -51,29 +60,22 @@ def _embed(path, items):
f.art = data
f.save()
options = {
'autoembed': True,
'maxwidth': 0,
}
class EmbedCoverArtPlugin(BeetsPlugin):
"""Allows albumart to be embedded into the actual files.
"""
def configure(self, config):
options['autoembed'] = \
ui.config_val(config, 'embedart', 'autoembed', True, bool)
options['maxwidth'] = \
int(ui.config_val(config, 'embedart', 'maxwidth', '0'))
if options['maxwidth'] and not ArtResizer.shared.local:
options['maxwidth'] = 0
log.error("embedart: ImageMagick or PIL not found; "
"'maxwidth' option ignored")
def __init__(self):
super(EmbedCoverArtPlugin, self).__init__()
if config['embedart']['maxwidth'].get(int) and \
not ArtResizer.shared.local:
config['embedart']['maxwidth'] = 0
log.warn("embedart: ImageMagick or PIL not found; "
"'maxwidth' option ignored")
def commands(self):
# Embed command.
embed_cmd = ui.Subcommand('embedart',
help='embed an image file into file metadata')
def embed_func(lib, config, opts, args):
def embed_func(lib, opts, args):
if not args:
raise ui.UserError('specify an image file')
imagepath = normpath(args.pop(0))
@ -85,7 +87,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
help='extract an image from file metadata')
extract_cmd.parser.add_option('-o', dest='outpath',
help='image output file')
def extract_func(lib, config, opts, args):
def extract_func(lib, opts, args):
outpath = normpath(opts.outpath or 'cover')
extract(lib, outpath, decargs(args))
extract_cmd.func = extract_func
@ -93,7 +95,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
# Clear command.
clear_cmd = ui.Subcommand('clearart',
help='remove images from file metadata')
def clear_func(lib, config, opts, args):
def clear_func(lib, opts, args):
clear(lib, decargs(args))
clear_cmd.func = clear_func
@ -155,6 +157,6 @@ def clear(lib, query):
# Automatically embed art into imported albums.
@EmbedCoverArtPlugin.listen('album_imported')
def album_imported(lib, album, config):
if album.artpath and options['autoembed']:
def album_imported(lib, album):
if album.artpath and config['embedart']['auto']:
_embed(album.artpath, album.items())

View file

@ -25,6 +25,7 @@ from beets.util.artresizer import ArtResizer
from beets import importer
from beets import ui
from beets import util
from beets import config
IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg']
COVER_NAMES = ['cover', 'front', 'art', 'album', 'folder']
@ -33,6 +34,13 @@ DOWNLOAD_EXTENSION = '.jpg'
log = logging.getLogger('beets')
config.add({
'fetchart': {
'auto': True,
'maxwidth': 0,
}
})
def _fetch_image(url):
"""Downloads an image from a URL and checks whether it seems to
@ -214,18 +222,15 @@ class FetchArtPlugin(BeetsPlugin):
# placing them in the filesystem.
self.art_paths = {}
def configure(self, config):
self.autofetch = ui.config_val(config, 'fetchart',
'autofetch', True, bool)
self.maxwidth = int(ui.config_val(config, 'fetchart',
'maxwidth', '0'))
self.autofetch = config['fetchart']['auto'].get(bool)
self.maxwidth = config['fetchart']['maxwidth'].get(int)
if self.autofetch:
# Enable two import hooks when fetching is enabled.
self.import_stages = [self.fetch_art]
self.register_listener('import_task_files', self.assign_art)
# Asynchronous; after music is added to the library.
def fetch_art(self, config, task):
def fetch_art(self, session, task):
"""Find art for the album being imported."""
if task.is_album: # Only fetch art for full albums.
if task.choice_flag == importer.action.ASIS:
@ -238,22 +243,23 @@ class FetchArtPlugin(BeetsPlugin):
# For any other choices (e.g., TRACKS), do nothing.
return
album = config.lib.get_album(task.album_id)
album = session.lib.get_album(task.album_id)
path = art_for_album(album, task.path, self.maxwidth, local)
if path:
self.art_paths[task] = path
# Synchronous; after music files are put in place.
def assign_art(self, config, task):
def assign_art(self, session, task):
"""Place the discovered art in the filesystem."""
if task in self.art_paths:
path = self.art_paths.pop(task)
album = config.lib.get_album(task.album_id)
album.set_art(path, not (config.delete or config.move))
if config.delete or config.move:
album = session.lib.get_album(task.album_id)
src_removed = config['import']['delete'].get(bool) or \
config['import']['move'].get(bool)
album.set_art(path, not src_removed)
if src_removed:
task.prune(path)
# Manual album art fetching.
@ -262,7 +268,7 @@ class FetchArtPlugin(BeetsPlugin):
cmd.parser.add_option('-f', '--force', dest='force',
action='store_true', default=False,
help='re-download art when already present')
def func(lib, config, opts, args):
def func(lib, opts, args):
batch_fetch_art(lib, lib.albums(ui.decargs(args)), opts.force,
self.maxwidth)
cmd.func = func

0
beetsplug/replaygain.py Executable file → Normal file
View file

0
beetsplug/scrub.py Executable file → Normal file
View file