Example #1
0
def list_items(lib, query, album, path, fmt):
    """Print out items in lib matching query. If album, then search for
    albums instead of single items. If path, print the matched objects'
    paths instead of human-readable information about them.
    """
    if fmt is None:
        # If no specific template is supplied, use a default.
        if album:
            fmt = u'$albumartist - $album'
        else:
            fmt = u'$artist - $album - $title'
    template = Template(fmt)

    if album:
        for album in lib.albums(query):
            if path:
                print_(album.item_dir())
            elif fmt is not None:
                print_(template.substitute(album._record))
    else:
        for item in lib.items(query):
            if path:
                print_(item.path)
            elif fmt is not None:
                print_(template.substitute(item.record))
Example #2
0
def _get_path_formats(config):
    """Returns a list of path formats (query/template pairs); reflecting
    the config's specified path formats.
    """
    legacy_path_format = config_val(config, 'beets', 'path_format', None)
    if legacy_path_format:
        # Old path formats override the default values.
        path_formats = [(library.PF_KEY_DEFAULT, Template(legacy_path_format))]
    else:
        # If no legacy path format, use the defaults instead.
        path_formats = DEFAULT_PATH_FORMATS

    if config.has_section('paths'):
        custom_path_formats = []
        for key, value in config.items('paths', True):
            if key in PF_KEY_QUERIES:
                # Special values that indicate simple queries.
                key = PF_KEY_QUERIES[key]
            elif key != library.PF_KEY_DEFAULT:
                # For non-special keys (literal queries), the _
                # character denotes a :.
                key = key.replace('_', ':')
            custom_path_formats.append((key, Template(value)))
        path_formats = custom_path_formats + path_formats

    return path_formats
Example #3
0
def list_items(lib, query, album, path, fmt):
    """Print out items in lib matching query. If album, then search for
    albums instead of single items. If path, print the matched objects'
    paths instead of human-readable information about them.
    """
    template = Template(fmt)

    if album:
        for album in lib.albums(query):
            if path:
                print_(album.item_dir())
            elif fmt is not None:
                print_(album.evaluate_template(template))
    else:
        for item in lib.items(query):
            if path:
                print_(item.path)
            elif fmt is not None:
                print_(item.evaluate_template(template, lib))
Example #4
0
    def destination(self, item, pathmod=None, in_album=False, fragment=False, basedir=None):
        """Returns the path in the library directory designated for item
        item (i.e., where the file ought to be). in_album forces the
        item to be treated as part of an album. fragment makes this
        method return just the path fragment underneath the root library
        directory; the path is also returned as Unicode instead of
        encoded as a bytestring. basedir can override the library's base
        directory for the destination.
        """
        pathmod = pathmod or os.path

        # Use a path format based on a query, falling back on the
        # default.
        for query, path_format in self.path_formats:
            if query == PF_KEY_DEFAULT:
                continue
            query = AndQuery.from_string(query)
            if in_album:
                # If we're treating this item as a member of the item,
                # hack the query so that singleton queries always
                # observe the item to be non-singleton.
                for i, subquery in enumerate(query):
                    if isinstance(subquery, SingletonQuery):
                        query[i] = FalseQuery() if subquery.sense else TrueQuery()
            if query.match(item):
                # The query matches the item! Use the corresponding path
                # format.
                break
        else:
            # No query matched; fall back to default.
            for query, path_format in self.path_formats:
                if query == PF_KEY_DEFAULT:
                    break
            else:
                assert False, "no default path format"
        subpath_tmpl = Template(path_format)

        # Get the item's Album if it has one.
        album = self.get_album(item)

        # Build the mapping for substitution in the path template,
        # beginning with the values from the database.
        mapping = {}
        for key in ITEM_KEYS_META:
            # Get the values from either the item or its album.
            if key in ALBUM_KEYS_ITEM and album is not None:
                # From album.
                value = getattr(album, key)
            else:
                # From Item.
                value = getattr(item, key)
            mapping[key] = util.sanitize_for_path(value, pathmod, key)

        # Use the album artist if the track artist is not set and
        # vice-versa.
        if not mapping["artist"]:
            mapping["artist"] = mapping["albumartist"]
        if not mapping["albumartist"]:
            mapping["albumartist"] = mapping["artist"]

        # Get values from plugins.
        for key, value in plugins.template_values(item).iteritems():
            mapping[key] = util.sanitize_for_path(value, pathmod, key)

        # Perform substitution.
        funcs = DefaultTemplateFunctions(self, item).functions()
        funcs.update(plugins.template_funcs())
        subpath = subpath_tmpl.substitute(mapping, funcs)

        # Encode for the filesystem, dropping unencodable characters.
        if isinstance(subpath, unicode) and not fragment:
            encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
            subpath = subpath.encode(encoding, "replace")

        # Truncate components and remove forbidden characters.
        subpath = util.sanitize_path(subpath, pathmod, self.replacements)

        # Preserve extension.
        _, extension = pathmod.splitext(item.path)
        subpath += extension.lower()

        if fragment:
            return subpath
        else:
            basedir = basedir or self.directory
            return normpath(os.path.join(basedir, subpath))
Example #5
0
# Constants.
CONFIG_PATH_VAR = 'BEETSCONFIG'
DEFAULT_CONFIG_FILENAME_UNIX = '.beetsconfig'
DEFAULT_CONFIG_FILENAME_WINDOWS = 'beetsconfig.ini'
DEFAULT_LIBRARY_FILENAME_UNIX = '.beetsmusic.blb'
DEFAULT_LIBRARY_FILENAME_WINDOWS = 'beetsmusic.blb'
DEFAULT_DIRECTORY_NAME = 'Music'
WINDOWS_BASEDIR = os.environ.get('APPDATA') or '~'
PF_KEY_QUERIES = {
    'comp': 'comp:true',
    'singleton': 'singleton:true',
}
DEFAULT_PATH_FORMATS = [
    (library.PF_KEY_DEFAULT,
     Template('$albumartist/$album%aunique{}/$track $title')),
    (PF_KEY_QUERIES['singleton'], Template('Non-Album/$artist/$title')),
    (PF_KEY_QUERIES['comp'],
     Template('Compilations/$album%aunique{}/$track $title')),
]
DEFAULT_ART_FILENAME = 'cover'
DEFAULT_TIMEOUT = 5.0
NULL_REPLACE = '<strip>'


# UI exception. Commands should throw this in order to display
# nonrecoverable errors to the user.
class UserError(Exception):
    pass

Example #6
0
    def destination(self, item, pathmod=None, in_album=False,
                    fragment=False, basedir=None):
        """Returns the path in the library directory designated for item
        item (i.e., where the file ought to be). in_album forces the
        item to be treated as part of an album. fragment makes this
        method return just the path fragment underneath the root library
        directory; the path is also returned as Unicode instead of
        encoded as a bytestring. basedir can override the library's base
        directory for the destination.
        """
        pathmod = pathmod or os.path

        # Use a path format based on a query, falling back on the
        # default.
        for query, path_format in self.path_formats:
            if query == PF_KEY_DEFAULT:
                continue
            query = AndQuery.from_string(query)
            if in_album:
                # If we're treating this item as a member of the item,
                # hack the query so that singleton queries always
                # observe the item to be non-singleton.
                for i, subquery in enumerate(query):
                    if isinstance(subquery, SingletonQuery):
                        query[i] = FalseQuery() if subquery.sense \
                                   else TrueQuery()
            if query.match(item):
                # The query matches the item! Use the corresponding path
                # format.
                break
        else:
            # No query matched; fall back to default.
            for query, path_format in self.path_formats:
                if query == PF_KEY_DEFAULT:
                    break
            else:
                assert False, "no default path format"
        subpath_tmpl = Template(path_format)

        # Get the item's Album if it has one.
        album = self.get_album(item)

        # Build the mapping for substitution in the path template,
        # beginning with the values from the database.
        mapping = {}
        for key in ITEM_KEYS_META:
            # Get the values from either the item or its album.
            if key in ALBUM_KEYS_ITEM and album is not None:
                # From album.
                value = getattr(album, key)
            else:
                # From Item.
                value = getattr(item, key)
            mapping[key] = util.sanitize_for_path(value, pathmod, key)

        # Use the album artist if the track artist is not set and
        # vice-versa.
        if not mapping['artist']:
            mapping['artist'] = mapping['albumartist']
        if not mapping['albumartist']:
            mapping['albumartist'] = mapping['artist']

        # Get values from plugins.
        for key, value in plugins.template_values(item).iteritems():
            mapping[key] = util.sanitize_for_path(value, pathmod, key)

        # Perform substitution.
        funcs = DefaultTemplateFunctions(self, item).functions()
        funcs.update(plugins.template_funcs())
        subpath = subpath_tmpl.substitute(mapping, funcs)

        # Encode for the filesystem, dropping unencodable characters.
        if isinstance(subpath, unicode) and not fragment:
            encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
            subpath = subpath.encode(encoding, 'replace')

        # Truncate components and remove forbidden characters.
        subpath = util.sanitize_path(subpath, pathmod, self.replacements)

        # Preserve extension.
        _, extension = pathmod.splitext(item.path)
        subpath += extension.lower()

        if fragment:
            return subpath
        else:
            basedir = basedir or self.directory
            return normpath(os.path.join(basedir, subpath))