mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 04:55:10 +01:00
begin Confit-ifying plugins in alphabetical order
This commit is contained in:
parent
e17cd6beba
commit
6c94358b13
11 changed files with 112 additions and 106 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
44
beetsplug/embedart.py
Executable file → Normal 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())
|
||||
|
|
|
|||
|
|
@ -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
0
beetsplug/replaygain.py
Executable file → Normal file
0
beetsplug/scrub.py
Executable file → Normal file
0
beetsplug/scrub.py
Executable file → Normal file
Loading…
Reference in a new issue