mirror of
https://github.com/beetbox/beets.git
synced 2025-12-30 12:32:33 +01:00
finish confit-ifying all the plugins
This commit is contained in:
parent
6c94358b13
commit
3ef9e006f4
17 changed files with 259 additions and 244 deletions
|
|
@ -163,7 +163,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
|
|||
# Hooks into import process.
|
||||
|
||||
@AcoustidPlugin.listen('import_task_start')
|
||||
def fingerprint_task(task):
|
||||
def fingerprint_task(task, session):
|
||||
"""Fingerprint each item in the task for later use during the
|
||||
autotagging candidate search.
|
||||
"""
|
||||
|
|
@ -172,7 +172,7 @@ def fingerprint_task(task):
|
|||
acoustid_match(item.path)
|
||||
|
||||
@AcoustidPlugin.listen('import_task_apply')
|
||||
def apply_acoustid_metadata(task):
|
||||
def apply_acoustid_metadata(task, session):
|
||||
"""Apply Acoustid metadata (fingerprint and ID) to the task's items.
|
||||
"""
|
||||
for item in task.imported_items():
|
||||
|
|
|
|||
|
|
@ -14,12 +14,18 @@
|
|||
|
||||
"""Like beet list, but with fuzzy matching
|
||||
"""
|
||||
import beets
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand, decargs, print_obj
|
||||
from beets.util.functemplate import Template
|
||||
from beets import config
|
||||
import difflib
|
||||
|
||||
config.add({
|
||||
'fuzzy': {
|
||||
'threshold': 0.7,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# THRESHOLD = 0.7
|
||||
|
||||
|
|
@ -42,7 +48,7 @@ def is_match(queryMatcher, item, album=False, verbose=False, threshold=0.7):
|
|||
return s >= threshold
|
||||
|
||||
|
||||
def fuzzy_list(lib, config, opts, args):
|
||||
def fuzzy_list(lib, opts, args):
|
||||
query = decargs(args)
|
||||
query = ' '.join(query).lower()
|
||||
queryMatcher = difflib.SequenceMatcher(b=query)
|
||||
|
|
@ -50,7 +56,7 @@ def fuzzy_list(lib, config, opts, args):
|
|||
if opts.threshold is not None:
|
||||
threshold = float(opts.threshold)
|
||||
else:
|
||||
threshold = float(conf['threshold'])
|
||||
threshold = config['fuzzy']['threshold'].as_number()
|
||||
|
||||
if opts.path:
|
||||
fmt = '$path'
|
||||
|
|
@ -67,9 +73,10 @@ def fuzzy_list(lib, config, opts, args):
|
|||
threshold=threshold), objs)
|
||||
|
||||
for item in items:
|
||||
print_obj(item, lib, config, template)
|
||||
print_obj(item, lib, template)
|
||||
if opts.verbose:
|
||||
print(is_match(queryMatcher, i, album=opts.album, verbose=True)[1])
|
||||
print(is_match(queryMatcher, item,
|
||||
album=opts.album, verbose=True)[1])
|
||||
|
||||
|
||||
fuzzy_cmd = Subcommand('fuzzy',
|
||||
|
|
@ -87,13 +94,7 @@ fuzzy_cmd.parser.add_option('-t', '--threshold', action='store',
|
|||
(default is 0.7)', default=None)
|
||||
fuzzy_cmd.func = fuzzy_list
|
||||
|
||||
conf = {}
|
||||
|
||||
|
||||
class Fuzzy(BeetsPlugin):
|
||||
def commands(self):
|
||||
return [fuzzy_cmd]
|
||||
|
||||
def configure(self, config):
|
||||
conf['threshold'] = beets.ui.config_val(config, 'fuzzy',
|
||||
'threshold', 0.7)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
import re
|
||||
import logging
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import config
|
||||
from beets.importer import action
|
||||
|
||||
|
||||
|
|
@ -25,6 +25,20 @@ __author__ = 'baobab@heresiarch.info'
|
|||
__version__ = '1.0'
|
||||
|
||||
|
||||
config.add({
|
||||
'ihate': {
|
||||
'warn_genre': [],
|
||||
'warn_artist': [],
|
||||
'warn_album': [],
|
||||
'warn_whitelist': [],
|
||||
'skip_genre': [],
|
||||
'skip_artist': [],
|
||||
'skip_album': [],
|
||||
'skip_whitelist': [],
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class IHatePlugin(BeetsPlugin):
|
||||
|
||||
_instance = None
|
||||
|
|
@ -45,40 +59,6 @@ class IHatePlugin(BeetsPlugin):
|
|||
cls).__new__(cls, *args, **kwargs)
|
||||
return cls._instance
|
||||
|
||||
def __str__(self):
|
||||
return ('(\n warn_genre = {0}\n'
|
||||
' warn_artist = {1}\n'
|
||||
' warn_album = {2}\n'
|
||||
' warn_whitelist = {3}\n'
|
||||
' skip_genre = {4}\n'
|
||||
' skip_artist = {5}\n'
|
||||
' skip_album = {6}\n'
|
||||
' skip_whitelist = {7} )\n'
|
||||
.format(self.warn_genre, self.warn_artist, self.warn_album,
|
||||
self.warn_whitelist, self.skip_genre, self.skip_artist,
|
||||
self.skip_album, self.skip_whitelist))
|
||||
|
||||
def configure(self, config):
|
||||
if not config.has_section('ihate'):
|
||||
self._log.debug('[ihate] plugin is not configured')
|
||||
return
|
||||
self.warn_genre = ui.config_val(config, 'ihate', 'warn_genre',
|
||||
'').split()
|
||||
self.warn_artist = ui.config_val(config, 'ihate', 'warn_artist',
|
||||
'').split()
|
||||
self.warn_album = ui.config_val(config, 'ihate', 'warn_album',
|
||||
'').split()
|
||||
self.warn_whitelist = ui.config_val(config, 'ihate', 'warn_whitelist',
|
||||
'').split()
|
||||
self.skip_genre = ui.config_val(config, 'ihate', 'skip_genre',
|
||||
'').split()
|
||||
self.skip_artist = ui.config_val(config, 'ihate', 'skip_artist',
|
||||
'').split()
|
||||
self.skip_album = ui.config_val(config, 'ihate', 'skip_album',
|
||||
'').split()
|
||||
self.skip_whitelist = ui.config_val(config, 'ihate', 'skip_whitelist',
|
||||
'').split()
|
||||
|
||||
@classmethod
|
||||
def match_patterns(cls, s, patterns):
|
||||
"""Check if string is matching any of the patterns in the list."""
|
||||
|
|
@ -99,44 +79,51 @@ class IHatePlugin(BeetsPlugin):
|
|||
except:
|
||||
genre = u''
|
||||
if genre and genre_patterns:
|
||||
if IHatePlugin.match_patterns(genre, genre_patterns):
|
||||
if cls.match_patterns(genre, genre_patterns):
|
||||
hate = True
|
||||
if not hate and task.cur_album and album_patterns:
|
||||
if IHatePlugin.match_patterns(task.cur_album, album_patterns):
|
||||
if cls.match_patterns(task.cur_album, album_patterns):
|
||||
hate = True
|
||||
if not hate and task.cur_artist and artist_patterns:
|
||||
if IHatePlugin.match_patterns(task.cur_artist, artist_patterns):
|
||||
if cls.match_patterns(task.cur_artist, artist_patterns):
|
||||
hate = True
|
||||
if hate and whitelist_patterns:
|
||||
if IHatePlugin.match_patterns(task.cur_artist, whitelist_patterns):
|
||||
if cls.match_patterns(task.cur_artist, whitelist_patterns):
|
||||
hate = False
|
||||
return hate
|
||||
|
||||
def job_to_do(self):
|
||||
"""Return True if at least one pattern is defined."""
|
||||
return any([self.warn_genre, self.warn_artist, self.warn_album,
|
||||
self.skip_genre, self.skip_artist, self.skip_album])
|
||||
return any(config['ihate'][l].get(list) for l in
|
||||
('warn_genre', 'warn_artist', 'warn_album',
|
||||
'skip_genre', 'skip_artist', 'skip_album'))
|
||||
|
||||
def import_task_choice_event(self, task, config):
|
||||
if task.choice_flag == action.APPLY:
|
||||
if self.job_to_do:
|
||||
if self.job_to_do():
|
||||
self._log.debug('[ihate] processing your hate')
|
||||
if self.do_i_hate_this(task, self.skip_genre, self.skip_artist,
|
||||
self.skip_album, self.skip_whitelist):
|
||||
if self.do_i_hate_this(task,
|
||||
config['ihate']['skip_genre'].get(list),
|
||||
config['ihate']['skip_artist'].get(list),
|
||||
config['ihate']['skip_album'].get(list),
|
||||
config['ihate']['skip_whitelist'].get(list)):
|
||||
task.choice_flag = action.SKIP
|
||||
self._log.info(u'[ihate] skipped: {0} - {1}'
|
||||
.format(task.cur_artist, task.cur_album))
|
||||
return
|
||||
if self.do_i_hate_this(task, self.warn_genre, self.warn_artist,
|
||||
self.warn_album, self.warn_whitelist):
|
||||
if self.do_i_hate_this(task,
|
||||
config['ihate']['warn_genre'].get(list),
|
||||
config['ihate']['warn_artist'].get(list),
|
||||
config['ihate']['warn_album'].get(list),
|
||||
config['ihate']['warn_whitelist'].get(list)):
|
||||
self._log.info(u'[ihate] you maybe hate this: {0} - {1}'
|
||||
.format(task.cur_artist, task.cur_album))
|
||||
else:
|
||||
self._log.debug('[ihate] nothing to do')
|
||||
else:
|
||||
self._log.debug('[ihate] user make a decision, nothing to do')
|
||||
self._log.debug('[ihate] user made a decision, nothing to do')
|
||||
|
||||
|
||||
@IHatePlugin.listen('import_task_choice')
|
||||
def ihate_import_task_choice(task, config):
|
||||
def ihate_import_task_choice(task, session):
|
||||
IHatePlugin().import_task_choice_event(task, config)
|
||||
|
|
|
|||
|
|
@ -19,26 +19,30 @@ import datetime
|
|||
import os
|
||||
import re
|
||||
|
||||
from beets import ui
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.util import normpath, syspath, bytestring_path
|
||||
from beets import config
|
||||
|
||||
M3U_DEFAULT_NAME = 'imported.m3u'
|
||||
|
||||
class ImportFeedsPlugin(BeetsPlugin):
|
||||
def configure(self, config):
|
||||
global _feeds_formats, _feeds_dir, _m3u_name
|
||||
config.add({
|
||||
'importfeeds': {
|
||||
'formats': [],
|
||||
'm3u_name': u'imported.m3u',
|
||||
'dir': None,
|
||||
}
|
||||
})
|
||||
|
||||
_feeds_formats = ui.config_val(config, 'importfeeds', 'feeds_formats',
|
||||
'').split()
|
||||
_m3u_name = ui.config_val(config, 'importfeeds', 'm3u_name',
|
||||
M3U_DEFAULT_NAME)
|
||||
_feeds_dir = ui.config_val(config, 'importfeeds', 'feeds_dir', None)
|
||||
class ImportFeedsPlugin(BeetsPlugin):
|
||||
def __init__(self):
|
||||
super(ImportFeedsPlugin, self).__init__()
|
||||
|
||||
if _feeds_dir:
|
||||
_feeds_dir = os.path.expanduser(bytestring_path(_feeds_dir))
|
||||
if not os.path.exists(_feeds_dir):
|
||||
os.makedirs(syspath(_feeds_dir))
|
||||
feeds_dir = config['importfeeds']['dir'].get()
|
||||
if feeds_dir:
|
||||
config['importfeeds']['dir'] = \
|
||||
os.path.expanduser(bytestring_path(feeds_dir))
|
||||
if not os.path.exists(syspath(feeds_dir)):
|
||||
os.makedirs(syspath(feeds_dir))
|
||||
|
||||
def _get_feeds_dir(lib):
|
||||
"""Given a Library object, return the path to the feeds directory to be
|
||||
|
|
@ -59,7 +63,10 @@ def _build_m3u_filename(basename):
|
|||
|
||||
basename = re.sub(r"[\s,'\"]", '_', basename)
|
||||
date = datetime.datetime.now().strftime("%Y%m%d_%Hh%M")
|
||||
path = normpath(os.path.join(_feeds_dir, date+'_'+basename+'.m3u'))
|
||||
path = normpath(os.path.join(
|
||||
config['importfeeds']['dir'].as_filename(),
|
||||
date + '_' + basename + '.m3u'
|
||||
))
|
||||
return path
|
||||
|
||||
def _write_m3u(m3u_path, items_paths):
|
||||
|
|
@ -72,35 +79,39 @@ def _write_m3u(m3u_path, items_paths):
|
|||
def _record_items(lib, basename, items):
|
||||
"""Records relative paths to the given items for each feed format
|
||||
"""
|
||||
|
||||
feedsdir = config['importfeeds']['dir'].as_filename()
|
||||
formats = config['importfeeds']['formats'].get(list)
|
||||
|
||||
paths = []
|
||||
for item in items:
|
||||
paths.append(os.path.relpath(item.path, _feeds_dir))
|
||||
paths.append(os.path.relpath(
|
||||
item.path, feedsdir
|
||||
))
|
||||
|
||||
if 'm3u' in _feeds_formats:
|
||||
m3u_path = os.path.join(_feeds_dir, _m3u_name)
|
||||
if 'm3u' in formats:
|
||||
basename = config['importfeeds']['m3u_name'].get(unicode).encode('utf8')
|
||||
m3u_path = os.path.join(feedsdir, basename)
|
||||
_write_m3u(m3u_path, paths)
|
||||
|
||||
if 'm3u_multi' in _feeds_formats:
|
||||
if 'm3u_multi' in formats:
|
||||
m3u_path = _build_m3u_filename(basename)
|
||||
_write_m3u(m3u_path, paths)
|
||||
|
||||
if 'link' in _feeds_formats:
|
||||
if 'link' in formats:
|
||||
for path in paths:
|
||||
dest = os.path.join(_feeds_dir, os.path.basename(path))
|
||||
dest = os.path.join(feedsdir, os.path.basename(path))
|
||||
if not os.path.exists(dest):
|
||||
os.symlink(path, dest)
|
||||
|
||||
@ImportFeedsPlugin.listen('library_opened')
|
||||
def library_opened(lib):
|
||||
global _feeds_dir
|
||||
if not _feeds_dir:
|
||||
_feeds_dir = _get_feeds_dir(lib)
|
||||
if config['importfeeds']['dir'].get() is None:
|
||||
config['importfeeds']['dir'] = _get_feeds_dir(lib)
|
||||
|
||||
@ImportFeedsPlugin.listen('album_imported')
|
||||
def album_imported(lib, album, config):
|
||||
def album_imported(lib, album):
|
||||
_record_items(lib, album.album, album.items())
|
||||
|
||||
@ImportFeedsPlugin.listen('item_imported')
|
||||
def item_imported(lib, item, config):
|
||||
def item_imported(lib, item):
|
||||
_record_items(lib, item.title, [item])
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ def info(paths):
|
|||
class InfoPlugin(BeetsPlugin):
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('info', help='show file metadata')
|
||||
def func(lib, config, opts, args):
|
||||
def func(lib, opts, args):
|
||||
if not args:
|
||||
raise ui.UserError('no file specified')
|
||||
info(args)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ import logging
|
|||
import traceback
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'pathfields': [],
|
||||
})
|
||||
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
|
|
@ -54,13 +59,12 @@ def compile_expr(expr):
|
|||
class InlinePlugin(BeetsPlugin):
|
||||
template_fields = {}
|
||||
|
||||
def configure(self, config):
|
||||
cls = type(self)
|
||||
def __init__(self):
|
||||
super(InlinePlugin, self).__init__()
|
||||
|
||||
# Add field expressions.
|
||||
if config.has_section('pathfields'):
|
||||
for key, value in config.items('pathfields', True):
|
||||
log.debug(u'adding template field %s' % key)
|
||||
func = compile_expr(value)
|
||||
if func is not None:
|
||||
cls.template_fields[key] = func
|
||||
for key, value in config['pathfields'].as_pairs():
|
||||
log.debug(u'adding template field %s' % key)
|
||||
func = compile_expr(value)
|
||||
if func is not None:
|
||||
InlinePlugin.template_fields[key] = func
|
||||
|
|
|
|||
|
|
@ -27,16 +27,24 @@ https://gist.github.com/1241307
|
|||
import logging
|
||||
import pylast
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from beets import plugins
|
||||
from beets import ui
|
||||
from beets.util import normpath
|
||||
from beets.ui import commands
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'lastgenre': {
|
||||
'whitelist': os.path.join(os.path.dirname(__file__), 'genres.txt'),
|
||||
'fallback': None,
|
||||
'canonical': None,
|
||||
}
|
||||
})
|
||||
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
LASTFM = pylast.LastFMNetwork(api_key=plugins.LASTFM_KEY)
|
||||
DEFAULT_WHITELIST = os.path.join(os.path.dirname(__file__), 'genres.txt')
|
||||
C14N_TREE = os.path.join(os.path.dirname(__file__), 'genres-tree.yaml')
|
||||
|
||||
PYLAST_EXCEPTIONS = (
|
||||
|
|
@ -125,23 +133,13 @@ options = {
|
|||
'branches': None,
|
||||
'c14n': False,
|
||||
}
|
||||
fallback_str = None
|
||||
class LastGenrePlugin(plugins.BeetsPlugin):
|
||||
def __init__(self):
|
||||
super(LastGenrePlugin, self).__init__()
|
||||
self.import_stages = [self.imported]
|
||||
|
||||
def configure(self, config):
|
||||
global fallback_str
|
||||
|
||||
wl_filename = ui.config_val(config, 'lastgenre', 'whitelist', None)
|
||||
if not wl_filename:
|
||||
# No filename specified. Instead, use the whitelist that's included
|
||||
# with the plugin (inside the package).
|
||||
wl_filename = DEFAULT_WHITELIST
|
||||
wl_filename = normpath(wl_filename)
|
||||
|
||||
# Read the whitelist file.
|
||||
wl_filename = config['lastgenre']['whitelist'].as_filename()
|
||||
whitelist = set()
|
||||
with open(wl_filename) as f:
|
||||
for line in f:
|
||||
|
|
@ -151,29 +149,25 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
options['whitelist'] = whitelist
|
||||
|
||||
# Read the genres tree for canonicalization if enabled.
|
||||
c14n_filename = ui.config_val(config, 'lastgenre', 'canonical', None)
|
||||
c14n_filename = config['lastgenre']['canonical'].get()
|
||||
if c14n_filename is not None:
|
||||
c14n_filename = c14n_filename.strip()
|
||||
if not c14n_filename:
|
||||
c14n_filename = C14N_TREE
|
||||
c14n_filename = normpath(c14n_filename)
|
||||
|
||||
from yaml import load
|
||||
genres_tree = load(open(c14n_filename, 'r'))
|
||||
genres_tree = yaml.load(open(c14n_filename, 'r'))
|
||||
branches = []
|
||||
flatten_tree(genres_tree, [], branches)
|
||||
options['branches'] = branches
|
||||
options['c14n'] = True
|
||||
|
||||
fallback_str = ui.config_val(config, 'lastgenre', 'fallback_str', None)
|
||||
|
||||
def commands(self):
|
||||
lastgenre_cmd = ui.Subcommand('lastgenre', help='fetch genres')
|
||||
def lastgenre_func(lib, config, 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 album in lib.albums(ui.decargs(args)):
|
||||
tags = []
|
||||
lastfm_obj = LASTFM.get_album(album.albumartist, album.album)
|
||||
|
|
@ -183,6 +177,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
tags.extend(_tags_for(lastfm_obj))
|
||||
genre = _tags_to_genre(tags)
|
||||
|
||||
fallback_str = config['lastgenre']['fallback'].get()
|
||||
if not genre and fallback_str != None:
|
||||
genre = fallback_str
|
||||
log.debug(u'no last.fm genre found: fallback to %s' % genre)
|
||||
|
|
@ -196,10 +191,10 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
lastgenre_cmd.func = lastgenre_func
|
||||
return [lastgenre_cmd]
|
||||
|
||||
def imported(self, config, task):
|
||||
def imported(self, session, task):
|
||||
tags = []
|
||||
if task.is_album:
|
||||
album = config.lib.get_album(task.album_id)
|
||||
album = session.lib.get_album(task.album_id)
|
||||
lastfm_obj = LASTFM.get_album(album.albumartist, album.album)
|
||||
if album.genre:
|
||||
tags.append(album.genre)
|
||||
|
|
@ -212,6 +207,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
tags.extend(_tags_for(lastfm_obj))
|
||||
genre = _tags_to_genre(tags)
|
||||
|
||||
fallback_str = config['lastgenre']['fallback'].get()
|
||||
if not genre and fallback_str != None:
|
||||
genre = fallback_str
|
||||
log.debug(u'no last.fm genre found: fallback to %s' % genre)
|
||||
|
|
@ -220,8 +216,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
log.debug(u'adding last.fm album genre: %s' % genre)
|
||||
|
||||
if task.is_album:
|
||||
album = config.lib.get_album(task.album_id)
|
||||
album = session.lib.get_album(task.album_id)
|
||||
album.genre = genre
|
||||
else:
|
||||
item.genre = genre
|
||||
config.lib.store(item)
|
||||
session.lib.store(item)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,13 @@ import logging
|
|||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets.ui import commands
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'lyrics': {
|
||||
'auto': True,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# Global logger.
|
||||
|
|
@ -203,11 +209,10 @@ class LyricsPlugin(BeetsPlugin):
|
|||
cmd.parser.add_option('-p', '--print', dest='printlyr',
|
||||
action='store_true', default=False,
|
||||
help='print lyrics 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_lyrics(lib, logging.INFO, item, write)
|
||||
if opts.printlyr and item.lyrics:
|
||||
|
|
@ -215,12 +220,8 @@ class LyricsPlugin(BeetsPlugin):
|
|||
cmd.func = func
|
||||
return [cmd]
|
||||
|
||||
def configure(self, config):
|
||||
global AUTOFETCH
|
||||
AUTOFETCH = ui.config_val(config, 'lyrics', 'autofetch', True, bool)
|
||||
|
||||
# Auto-fetch lyrics on import.
|
||||
def imported(self, config, task):
|
||||
if AUTOFETCH:
|
||||
def imported(self, session, task):
|
||||
if config['lyrics']['auto']:
|
||||
for item in task.imported_items():
|
||||
fetch_item_lyrics(config.lib, logging.DEBUG, item, False)
|
||||
fetch_item_lyrics(session.lib, logging.DEBUG, item, False)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from __future__ import print_function
|
|||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand
|
||||
from beets import ui
|
||||
from beets import config
|
||||
import musicbrainzngs
|
||||
from musicbrainzngs import musicbrainz
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ def submit_albums(collection_id, release_ids):
|
|||
# A non-empty request body is required to avoid a 411 "Length
|
||||
# Required" error from the MB server.
|
||||
|
||||
def update_collection(lib, config, opts, args):
|
||||
def update_collection(lib, opts, args):
|
||||
# Get the collection to modify.
|
||||
collections = musicbrainz._mb_request('collection', 'GET', True, True)
|
||||
if not collections['collection-list']:
|
||||
|
|
@ -55,10 +56,12 @@ update_mb_collection_cmd = Subcommand('mbupdate',
|
|||
update_mb_collection_cmd.func = update_collection
|
||||
|
||||
class MusicBrainzCollectionPlugin(BeetsPlugin):
|
||||
def configure(self, config):
|
||||
username = ui.config_val(config, 'musicbrainz', 'user', '')
|
||||
password = ui.config_val(config, 'musicbrainz', 'pass', '')
|
||||
musicbrainzngs.auth(username, password)
|
||||
def __init__(self):
|
||||
super(MusicBrainzCollectionPlugin, self).__init__()
|
||||
musicbrainzngs.auth(
|
||||
config['musicbrainz']['user'].get(unicode),
|
||||
config['musicbrainz']['pass'].get(unicode)
|
||||
)
|
||||
|
||||
def commands(self):
|
||||
return [update_mb_collection_cmd]
|
||||
|
|
|
|||
|
|
@ -23,8 +23,16 @@ Put something like the following in your .beetsconfig to configure:
|
|||
from __future__ import print_function
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
import socket
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'mpdupdate': {
|
||||
'host': u'localhost',
|
||||
'port': 6600,
|
||||
'password': u'',
|
||||
}
|
||||
})
|
||||
|
||||
# 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
|
||||
|
|
@ -88,20 +96,13 @@ def update_mpd(host='localhost', port=6600, password=None):
|
|||
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', '')
|
||||
pass
|
||||
|
||||
@MPDUpdatePlugin.listen('import')
|
||||
def update(lib=None, paths=None):
|
||||
update_mpd(options['host'], options['port'], options['password'])
|
||||
update_mpd(
|
||||
config['mpdupdate']['host'].get(unicode),
|
||||
config['mpdupdate']['port'].get(int),
|
||||
config['mpdupdate']['password'].get(unicode),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ from beets.ui import Subcommand, decargs, print_obj
|
|||
from beets.util.functemplate import Template
|
||||
import random
|
||||
|
||||
def random_item(lib, config, opts, args):
|
||||
def random_item(lib, opts, args):
|
||||
query = decargs(args)
|
||||
if opts.path:
|
||||
fmt = '$path'
|
||||
|
|
@ -35,7 +35,7 @@ def random_item(lib, config, opts, args):
|
|||
objs = random.sample(objs, number)
|
||||
|
||||
for item in objs:
|
||||
print_obj(item, lib, config, template)
|
||||
print_obj(item, lib, template)
|
||||
|
||||
random_cmd = Subcommand('random',
|
||||
help='chose a random track or album')
|
||||
|
|
|
|||
|
|
@ -19,11 +19,22 @@ import os
|
|||
from beets import ui
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.util import syspath, command_output
|
||||
from beets.ui import commands
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'replaygain': {
|
||||
'overwrite': False,
|
||||
'albumgain': False,
|
||||
'noclip': True,
|
||||
'apply_gain': False,
|
||||
'targetlevel': 89,
|
||||
'auto': True,
|
||||
'command': u'',
|
||||
}
|
||||
})
|
||||
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
DEFAULT_REFERENCE_LOUDNESS = 89
|
||||
SAMPLE_MAX = 1 << 15
|
||||
|
||||
class ReplayGainError(Exception):
|
||||
|
|
@ -68,23 +79,15 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
super(ReplayGainPlugin, self).__init__()
|
||||
self.import_stages = [self.imported]
|
||||
|
||||
def configure(self, config):
|
||||
self.overwrite = ui.config_val(config, 'replaygain',
|
||||
'overwrite', False, bool)
|
||||
self.albumgain = ui.config_val(config, 'replaygain',
|
||||
'albumgain', False, bool)
|
||||
self.noclip = ui.config_val(config, 'replaygain',
|
||||
'noclip', True, bool)
|
||||
self.apply_gain = ui.config_val(config, 'replaygain',
|
||||
'apply_gain', False, bool)
|
||||
target_level = float(ui.config_val(config, 'replaygain',
|
||||
'targetlevel',
|
||||
DEFAULT_REFERENCE_LOUDNESS))
|
||||
self.gain_offset = int(target_level - DEFAULT_REFERENCE_LOUDNESS)
|
||||
self.automatic = ui.config_val(config, 'replaygain',
|
||||
'automatic', True, bool)
|
||||
self.overwrite = config['replaygain']['overwrite'].get(bool)
|
||||
self.albumgain = config['replaygain']['albumgain'].get(bool)
|
||||
self.noclip = config['replaygain']['noclip'].get(bool)
|
||||
self.apply_gain = config['replaygain']['apply_gain'].get(bool)
|
||||
target_level = config['replaygain']['targetlevel'].as_number()
|
||||
self.gain_offset = int(target_level - 89)
|
||||
self.automatic = config['replaygain']['auto'].get(bool)
|
||||
self.command = config['replaygain']['command'].get(unicode)
|
||||
|
||||
self.command = ui.config_val(config,'replaygain','command', None)
|
||||
if self.command:
|
||||
# Explicit executable path.
|
||||
if not os.path.isfile(self.command):
|
||||
|
|
@ -106,27 +109,26 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
'no replaygain command found: install mp3gain or aacgain'
|
||||
)
|
||||
|
||||
def imported(self, config, task):
|
||||
def imported(self, session, task):
|
||||
"""Our import stage function."""
|
||||
if not self.automatic:
|
||||
return
|
||||
|
||||
if task.is_album:
|
||||
album = config.lib.get_album(task.album_id)
|
||||
album = session.lib.get_album(task.album_id)
|
||||
items = list(album.items())
|
||||
else:
|
||||
items = [task.item]
|
||||
|
||||
results = self.compute_rgain(items, task.is_album)
|
||||
if results:
|
||||
self.store_gain(config.lib, items, results,
|
||||
self.store_gain(session.lib, items, results,
|
||||
album if task.is_album else None)
|
||||
|
||||
def commands(self):
|
||||
"""Provide a ReplayGain command."""
|
||||
def func(lib, config, opts, args):
|
||||
write = ui.config_val(config, 'beets', 'import_write',
|
||||
commands.DEFAULT_IMPORT_WRITE, bool)
|
||||
def func(lib, opts, args):
|
||||
write = config['import']['write'].get(bool)
|
||||
|
||||
if opts.album:
|
||||
# Analyze albums.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ from collections import defaultdict
|
|||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import library
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'rewrite': {},
|
||||
})
|
||||
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
|
|
@ -41,16 +46,13 @@ def rewriter(field, rules):
|
|||
return fieldfunc
|
||||
|
||||
class RewritePlugin(BeetsPlugin):
|
||||
template_fields = {}
|
||||
|
||||
def configure(self, config):
|
||||
cls = type(self)
|
||||
def __init__(self):
|
||||
super(BeetsPlugin, self).__init__()
|
||||
BeetsPlugin.template_fields = {}
|
||||
|
||||
# Gather all the rewrite rules for each field.
|
||||
rules = defaultdict(list)
|
||||
if not config.has_section('rewrite'):
|
||||
return
|
||||
for key, value in config.items('rewrite', True):
|
||||
for key, value in config['rewrite'].items():
|
||||
try:
|
||||
fieldname, pattern = key.split(None, 1)
|
||||
except ValueError:
|
||||
|
|
@ -68,4 +70,5 @@ class RewritePlugin(BeetsPlugin):
|
|||
|
||||
# Replace each template field with the new rewriter function.
|
||||
for fieldname, fieldrules in rules.iteritems():
|
||||
cls.template_fields[fieldname] = rewriter(fieldname, fieldrules)
|
||||
RewritePlugin.template_fields[fieldname] = \
|
||||
rewriter(fieldname, fieldrules)
|
||||
|
|
|
|||
|
|
@ -20,10 +20,16 @@ import logging
|
|||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import util
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'scrub': {
|
||||
'auto': True,
|
||||
}
|
||||
})
|
||||
|
||||
log = logging.getLogger('beets')
|
||||
|
||||
AUTOSCRUB_KEY = 'autoscrub'
|
||||
_MUTAGEN_FORMATS = {
|
||||
'asf': 'ASF',
|
||||
'apev2': 'APEv2File',
|
||||
|
|
@ -42,17 +48,10 @@ _MUTAGEN_FORMATS = {
|
|||
|
||||
scrubbing = False
|
||||
|
||||
options = {
|
||||
AUTOSCRUB_KEY: True,
|
||||
}
|
||||
class ScrubPlugin(BeetsPlugin):
|
||||
"""Removes extraneous metadata from files' tags."""
|
||||
def configure(self, config):
|
||||
options[AUTOSCRUB_KEY] = \
|
||||
ui.config_val(config, 'scrub', AUTOSCRUB_KEY, True, bool)
|
||||
|
||||
def commands(self):
|
||||
def scrub_func(lib, config, opts, args):
|
||||
def scrub_func(lib, opts, args):
|
||||
# This is a little bit hacky, but we set a global flag to
|
||||
# avoid autoscrubbing when we're also explicitly scrubbing.
|
||||
global scrubbing
|
||||
|
|
@ -107,6 +106,6 @@ def _scrub(path):
|
|||
# Automatically embed art into imported albums.
|
||||
@ScrubPlugin.listen('write')
|
||||
def write_item(item):
|
||||
if not scrubbing and options[AUTOSCRUB_KEY]:
|
||||
if not scrubbing and config['scrub']['auto']:
|
||||
log.debug(u'auto-scrubbing %s' % util.displayable_path(item.path))
|
||||
_scrub(item.path)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,17 @@
|
|||
import re
|
||||
import logging
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import config
|
||||
|
||||
config.add({
|
||||
'the': {
|
||||
'the': True,
|
||||
'a': True,
|
||||
'format': u'{0}, {1}',
|
||||
'strip': False,
|
||||
'patterns': [],
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
__author__ = 'baobab@heresiarch.info'
|
||||
|
|
@ -44,21 +54,10 @@ class ThePlugin(BeetsPlugin):
|
|||
cls).__new__(cls, *args, **kwargs)
|
||||
return cls._instance
|
||||
|
||||
def __str__(self):
|
||||
return ('[the]\n the = {0}\n a = {1}\n format = {2}\n'
|
||||
' strip = {3}\n patterns = {4}'
|
||||
.format(self.the, self.a, self.format, self.strip,
|
||||
self.patterns))
|
||||
def __init__(self):
|
||||
super(ThePlugin, self).__init__()
|
||||
|
||||
def configure(self, config):
|
||||
if not config.has_section('the'):
|
||||
self._log.debug(u'[the] plugin is not configured, using defaults')
|
||||
return
|
||||
self.the = ui.config_val(config, 'the', 'the', True, bool)
|
||||
self.a = ui.config_val(config, 'the', 'a', True, bool)
|
||||
self.format = ui.config_val(config, 'the', 'format', FORMAT)
|
||||
self.strip = ui.config_val(config, 'the', 'strip', False, bool)
|
||||
self.patterns = ui.config_val(config, 'the', 'patterns', '').split()
|
||||
self.patterns = config['the']['patterns'].get(list)
|
||||
for p in self.patterns:
|
||||
if p:
|
||||
try:
|
||||
|
|
@ -69,9 +68,9 @@ class ThePlugin(BeetsPlugin):
|
|||
if not (p.startswith('^') or p.endswith('$')):
|
||||
self._log.warn(u'[the] warning: \"{0}\" will not '
|
||||
'match string start/end'.format(p))
|
||||
if self.a:
|
||||
if config['the']['a']:
|
||||
self.patterns = [PATTERN_A] + self.patterns
|
||||
if self.the:
|
||||
if config['the']['the']:
|
||||
self.patterns = [PATTERN_THE] + self.patterns
|
||||
if not self.patterns:
|
||||
self._log.warn(u'[the] no patterns defined!')
|
||||
|
|
@ -93,10 +92,11 @@ class ThePlugin(BeetsPlugin):
|
|||
return text
|
||||
else:
|
||||
r = re.sub(r, '', text).strip()
|
||||
if self.strip:
|
||||
if config['the']['strip']:
|
||||
return r
|
||||
else:
|
||||
return self.format.format(r, t.strip()).strip()
|
||||
fmt = config['the']['format'].get(unicode)
|
||||
return fmt.format(r, t.strip()).strip()
|
||||
else:
|
||||
return u''
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,18 @@
|
|||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import util
|
||||
from beets import config
|
||||
import beets.library
|
||||
import flask
|
||||
from flask import g
|
||||
import os
|
||||
|
||||
DEFAULT_HOST = ''
|
||||
DEFAULT_PORT = 8337
|
||||
config.add({
|
||||
'web': {
|
||||
'host': u'',
|
||||
'port': 8337,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# Utilities.
|
||||
|
|
@ -126,14 +131,15 @@ class WebPlugin(BeetsPlugin):
|
|||
cmd = ui.Subcommand('web', help='start a Web interface')
|
||||
cmd.parser.add_option('-d', '--debug', action='store_true',
|
||||
default=False, help='debug mode')
|
||||
def func(lib, config, opts, args):
|
||||
host = args.pop(0) if args else \
|
||||
beets.ui.config_val(config, 'web', 'host', DEFAULT_HOST)
|
||||
port = args.pop(0) if args else \
|
||||
beets.ui.config_val(config, 'web', 'port', str(DEFAULT_PORT))
|
||||
port = int(port)
|
||||
def func(lib, opts, args):
|
||||
if args:
|
||||
config['web']['host'] = args.pop(0)
|
||||
if args:
|
||||
config['web']['port'] = int(args.pop(0))
|
||||
|
||||
app.config['lib'] = lib
|
||||
app.run(host=host, port=port, debug=opts.debug, threaded=True)
|
||||
app.run(host=config['web']['host'].get(unicode),
|
||||
port=config['web']['port'].get(int),
|
||||
debug=opts.debug, threaded=True)
|
||||
cmd.func = func
|
||||
return [cmd]
|
||||
|
|
|
|||
|
|
@ -17,9 +17,16 @@
|
|||
import re
|
||||
import logging
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets.library import ITEM_KEYS
|
||||
from beets.importer import action
|
||||
from beets import config
|
||||
from beets.util import confit
|
||||
|
||||
config.add({
|
||||
'zero': {
|
||||
'fields': [],
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
__author__ = 'baobab@heresiarch.info'
|
||||
|
|
@ -31,36 +38,30 @@ class ZeroPlugin(BeetsPlugin):
|
|||
_instance = None
|
||||
_log = logging.getLogger('beets')
|
||||
|
||||
fields = []
|
||||
patterns = {}
|
||||
warned = False
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(ZeroPlugin,
|
||||
cls).__new__(cls, *args, **kwargs)
|
||||
return cls._instance
|
||||
|
||||
def __str__(self):
|
||||
return ('[zero]\n fields = {0}\n patterns = {1}\n warned = {2}'
|
||||
.format(self.fields, self.patterns, self.warned))
|
||||
def __init__(self):
|
||||
super(ZeroPlugin, self).__init__()
|
||||
|
||||
def configure(self, config):
|
||||
if not config.has_section('zero'):
|
||||
self._log.debug('[zero] plugin is not configured')
|
||||
return
|
||||
for f in ui.config_val(config, 'zero', 'fields', '').split():
|
||||
self.fields = []
|
||||
self.patterns = {}
|
||||
self.warned = False
|
||||
|
||||
for f in config['zero']['fields'].get(list):
|
||||
if f not in ITEM_KEYS:
|
||||
self._log.error('[zero] invalid field: {0}'.format(f))
|
||||
else:
|
||||
self.fields.append(f)
|
||||
p = ui.config_val(config, 'zero', f, '').split()
|
||||
if p:
|
||||
self.patterns[f] = p
|
||||
else:
|
||||
self.patterns[f] = ['.']
|
||||
try:
|
||||
self.patterns[f] = config['zero'][f].get(list)
|
||||
except confit.NotFoundError:
|
||||
self.patterns[f] = [u'']
|
||||
|
||||
def import_task_choice_event(self, task, config):
|
||||
def import_task_choice_event(self, task):
|
||||
"""Listen for import_task_choice event."""
|
||||
if task.choice_flag == action.ASIS and not self.warned:
|
||||
self._log.warn('[zero] cannot zero in \"as-is\" mode')
|
||||
|
|
@ -73,7 +74,7 @@ class ZeroPlugin(BeetsPlugin):
|
|||
the list.
|
||||
"""
|
||||
for p in patterns:
|
||||
if re.findall(p, unicode(field), flags=re.IGNORECASE):
|
||||
if re.search(p, unicode(field), flags=re.IGNORECASE):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -100,8 +101,8 @@ class ZeroPlugin(BeetsPlugin):
|
|||
|
||||
|
||||
@ZeroPlugin.listen('import_task_choice')
|
||||
def zero_choice(task, config):
|
||||
ZeroPlugin().import_task_choice_event(task, config)
|
||||
def zero_choice(session, task):
|
||||
ZeroPlugin().import_task_choice_event(task)
|
||||
|
||||
@ZeroPlugin.listen('write')
|
||||
def zero_write(item):
|
||||
|
|
|
|||
Loading…
Reference in a new issue