Exemplo n.º 1
0
Arquivo: rdm.py Projeto: Klimaks/beets
def random_item(lib, config, opts, args):
    query = decargs(args)
    path = opts.path
    fmt = opts.format

    if fmt is None:
        # If no specific template is supplied, use a default
        if opts.album:
            fmt = u'$albumartist - $album'
        else:
            fmt = u'$artist - $album - $title'
    template = Template(fmt)

    if opts.album:
        objs = list(lib.albums(query=query))
    else:
        objs = list(lib.items(query=query))
    number = min(len(objs), opts.number)
    objs = random.sample(objs, number)

    if opts.album:
        for album in objs:
            if path:
                print_(album.item_dir())
            else:
                print_(template.substitute(album._record))
    else:
        for item in objs:
            if path:
                print_(item.path)
            else:
                print_(template.substitute(item.record))
Exemplo n.º 2
0
    def _destination(self, filename, mapping):
        '''Returns a destination path a file should be moved to. The filename
        is unique to ensure files aren't overwritten. This also checks the
        config for path formats based on file extension allowing the use of
        beets' template functions. If no path formats are found for the file
        extension the original filename is used with the album path.
            - ripped from beets/library.py
        '''
        file_ext = os.path.splitext(filename)[1]

        for query, path_format in self.path_formats:
            query_ext = '.' + query[4:]
            if query_ext == file_ext.decode('utf8'):
                break
        else:
            # No query matched; use original filename
            file_path = os.path.join(mapping['albumpath'],
                                     beets.util.displayable_path(filename))
            return file_path

        if isinstance(path_format, Template):
            subpath_tmpl = path_format
        else:
            subpath_tmpl = Template(path_format)

        # Get template funcs and evaluate against mapping
        funcs = DefaultTemplateFunctions().functions()
        file_path = subpath_tmpl.substitute(mapping, funcs) + file_ext.decode('utf8')

        # Sanitize filename
        filename = beets.util.sanitize_path(os.path.basename(file_path))
        dirname = os.path.dirname(file_path)
        file_path = os.path.join(dirname, filename)

        return file_path
Exemplo n.º 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.
    """
    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))
    def _destination(self, filename, mapping):
        '''Returns a destination path a file should be moved to. The filename
        is unique to ensure files aren't overwritten. This also checks the
        config for path formats based on file extension allowing the use of
        beets' template functions. If no path formats are found for the file
        extension the original filename is used with the album path.
            - ripped from beets/library.py
        '''
        file_ext = os.path.splitext(filename)[1]

        for query, path_format in self.path_formats:
            query_ext = '.' + query[4:]
            if query_ext == file_ext:
                break
        else:
            # No query matched; use original filename
            if self.remaining_folder!=[]:
                #remaining_folder = self.remaining_folder.replace('/','')+'/' # Make sure there is a trailing slash
                remaining_folder = self.remaining_folder+'/' # Make sure there is a trailing slash
                file_path = os.path.join(mapping['albumpath'],remaining_folder,
                                     beets.util.displayable_path(filename))
            else:
                file_path = os.path.join(mapping['albumpath'],
                                     beets.util.displayable_path(filename))
            return file_path

        if isinstance(path_format, Template):
            subpath_tmpl = path_format
        else:
            subpath_tmpl = Template(path_format)

        # Get template funcs and evaluate against mapping
        funcs = DefaultTemplateFunctions().functions()
        file_path = subpath_tmpl.substitute(mapping, funcs) + file_ext
        return file_path
Exemplo n.º 5
0
 def evaluate_template(self, template, for_path=False):
     """Evaluate a template (a string or a `Template` object) using
     the object's fields. If `for_path` is true, then no new path
     separators will be added to the template.
     """
     # Perform substitution.
     if isinstance(template, basestring):
         template = Template(template)
     return template.substitute(self.formatted(for_path),
                                self._template_funcs())
Exemplo n.º 6
0
def list_items(lib, query, album, fmt, config):
    """Print out items in lib matching query. If album, then search for
    albums instead of single items.
    """
    tmpl = Template(fmt) if fmt else Template(ui._pick_format(config, album))
    if album:
        for album in lib.albums(query):
            ui.print_obj(album, lib, config, tmpl)
    else:
        for item in lib.items(query):
            ui.print_obj(item, lib, config, tmpl)
Exemplo n.º 7
0
    def evaluate_template(self, template, for_path=False):
        """Evaluate a template (a string or a `Template` object) using
        the object's fields. If `for_path` is true, then no new path
        separators will be added to the template.
        """
        # Build value mapping.
        mapping = self._formatted_mapping(for_path)

        # Get template functions.
        funcs = self._template_funcs()

        # Perform substitution.
        if isinstance(template, basestring):
            template = Template(template)
        return template.substitute(mapping, funcs)
Exemplo n.º 8
0
    def evaluate_template(self, template, for_path=False):
        """Evaluate a template (a string or a `Template` object) using
        the object's fields. If `for_path` is true, then no new path
        separators will be added to the template.
        """
        # Build value mapping.
        mapping = self._formatted_mapping(for_path)

        # Get template functions.
        funcs = self._template_funcs()

        # Perform substitution.
        if isinstance(template, basestring):
            template = Template(template)
        return template.substitute(mapping, funcs)
Exemplo n.º 9
0
def random_item(lib, config, opts, args):
    query = decargs(args)
    path = opts.path
    fmt = opts.format

    if fmt is None:
        # If no specific template is supplied, use a default
        if opts.album:
            fmt = u'$albumartist - $album'
        else:
            fmt = u'$artist - $album - $title'
    template = Template(fmt)

    if opts.album:
        objs = list(lib.albums(query=query))
    else:
        objs = list(lib.items(query=query))
    number = min(len(objs), opts.number)
    objs = random.sample(objs, number)

    if opts.album:
        for album in objs:
            if path:
                print_(album.item_dir())
            else:
                print_(album.evaluate_template(template))
    else:
        for item in objs:
            if path:
                print_(item.path)
            else:
                print_(item.evaluate_template(template, lib))
Exemplo n.º 10
0
def update_playlists(lib):
    ui.print_("Updating smart playlists...")
    playlists = config['smartplaylist']['playlists'].get(list)
    playlist_dir = config['smartplaylist']['playlist_dir'].as_filename()
    relative_to = config['smartplaylist']['relative_to'].get()
    if relative_to:
        relative_to = normpath(relative_to)

    for playlist in playlists:
        items = lib.items(playlist['query'])
        m3us = {}
        basename = playlist['name'].encode('utf8')
        # As we allow tags in the m3u names, we'll need to iterate through
        # the items and generate the correct m3u file names.
        for item in items:
            m3u_name = item.evaluate_template(Template(basename),
                                              lib=lib,
                                              sanitize=True)
            if not (m3u_name in m3us):
                m3us[m3u_name] = []
            if relative_to:
                m3us[m3u_name].append(os.path.relpath(item.path, relative_to))
            else:
                m3us[m3u_name].append(item.path)
        # Now iterate through the m3us that we need to generate
        for m3u in m3us:
            m3u_path = normpath(os.path.join(playlist_dir, m3u))
            with open(syspath(m3u_path), 'w') as f:
                for path in m3us[m3u]:
                    f.write(path + '\n')
    ui.print_("... Done")
Exemplo n.º 11
0
def fuzzy_list(lib, config, opts, args):
    query = decargs(args)
    query = ' '.join(query).lower()
    queryMatcher = difflib.SequenceMatcher(b=query)

    if opts.threshold is not None:
        threshold = float(opts.threshold)
    else:
        threshold = float(conf['threshold'])

    if opts.path:
        fmt = '$path'
    else:
        fmt = opts.format
    template = Template(fmt) if fmt else None

    if opts.album:
        objs = lib.albums()
    else:
        objs = lib.items()

    items = filter(lambda i: is_match(queryMatcher, i, album=opts.album,
                                      threshold=threshold), objs)

    for item in items:
        print_obj(item, lib, config, template)
        if opts.verbose:
            print(is_match(queryMatcher, i, album=opts.album, verbose=True)[1])
Exemplo n.º 12
0
def get_path_formats():
    """Get the configuration's path formats as a list of query/template
    pairs.
    """
    path_formats = []
    for query, view in config['paths'].items():
        query = PF_KEY_QUERIES.get(query, query)  # Expand common queries.
        path_formats.append((query, Template(view.get(unicode))))
    return path_formats
Exemplo n.º 13
0
def get_path_formats(subview=None):
    """Get the configuration's path formats as a list of query/template
    pairs.
    """
    path_formats = []
    subview = subview or config['paths']
    for query, view in subview.items():
        query = PF_KEY_QUERIES.get(query, query)  # Expand common queries.
        path_formats.append((query, Template(view.as_str())))
    return path_formats
Exemplo n.º 14
0
def print_obj(obj, lib, fmt=None):
    """Print an Album or Item object. If `fmt` is specified, use that
    format string. Otherwise, use the configured template.
    """
    album = isinstance(obj, library.Album)
    fmt = _pick_format(album, fmt)
    if isinstance(fmt, Template):
        template = fmt
    else:
        template = Template(fmt)
    print_(obj.evaluate_template(template))
Exemplo n.º 15
0
def _get_art_filename(config):
    """Returns a string of album art format; reflecting
    the config's specified album art filename.
    """
    legacy_art_filename = config_val(config, 'beets', 'art_filename', None)
    if legacy_art_filename:
        # Old art filename format override the default value.
        art_filename = Template(legacy_art_filename)
    else:
        # If no legacy art filename format, use the default instead.
        art_filename = DEFAULT_ART_FILENAME
 
    return art_filename
Exemplo n.º 16
0
def print_obj(obj, lib, config, fmt=None):
    """Print an Album or Item object. If `fmt` is specified, use that
    format string. Otherwise, use the configured (or default) template.
    """
    album = isinstance(obj, library.Album)
    if not fmt:
        fmt = _pick_format(config, album=album)
    if isinstance(fmt, Template):
        template = fmt
    else:
        template = Template(fmt)
    if album:
        print_(obj.evaluate_template(template))
    else:
        print_(obj.evaluate_template(template, lib=lib))
Exemplo n.º 17
0
    def _destination(self, filename, mapping):
        '''Returns a destination path a file should be moved to. The filename
        is unique to ensure files aren't overwritten. This also checks the
        config for path formats based on file extension allowing the use of
        beets' template functions. If no path formats are found for the file
        extension the original filename is used with the album path.
            - ripped from beets/library.py
        '''
        file_ext = os.path.splitext(filename)[1]

        for query, path_format in self.path_formats:
            query_ext = '.' + query[4:]
            if query_ext == file_ext.decode('utf8'):
                break
        else:
            # No query matched; use original filename
            file_path = os.path.join(mapping['albumpath'].encode('utf-8'),
                                     filename)
            return file_path

        if isinstance(path_format, Template):
            subpath_tmpl = path_format
        else:
            subpath_tmpl = Template(path_format)

        # Get template funcs and evaluate against mapping
        funcs = DefaultTemplateFunctions().functions()
        file_path = subpath_tmpl.substitute(mapping,
                                            funcs) + file_ext.decode('utf8')

        # Sanitize filename
        filename = beets.util.sanitize_path(os.path.basename(file_path))
        dirname = os.path.dirname(file_path)
        file_path = os.path.join(dirname, filename)

        return file_path
Exemplo n.º 18
0
def random_item(lib, config, opts, args):
    query = decargs(args)
    if opts.path:
        fmt = '$path'
    else:
        fmt = opts.format
    template = Template(fmt) if fmt else None

    if opts.album:
        objs = list(lib.albums(query=query))
    else:
        objs = list(lib.items(query=query))
    number = min(len(objs), opts.number)
    objs = random.sample(objs, number)

    for item in objs:
        print_obj(item, lib, config, template)
Exemplo n.º 19
0
def random_item(lib, opts, args):
    query = decargs(args)
    if opts.path:
        fmt = '$path'
    else:
        fmt = opts.format
    template = Template(fmt) if fmt else None

    if opts.album:
        objs = list(lib.albums(query=query))
    else:
        objs = list(lib.items(query=query))

    if opts.equal_chance:
        # Group the objects by artist so we can sample from them.
        key = attrgetter('albumartist')
        objs.sort(key=key)
        objs_by_artists = {}
        for artist, v in groupby(objs, key):
            objs_by_artists[artist] = list(v)

        objs = []
        for _ in range(opts.number):
            # Terminate early if we're out of objects to select.
            if not objs_by_artists:
                break

            # Choose an artist and an object for that artist, removing
            # this choice from the pool.
            artist = random.choice(objs_by_artists.keys())
            objs_from_artist = objs_by_artists[artist]
            i = random.randint(0, len(objs_from_artist) - 1)
            objs.append(objs_from_artist.pop(i))

            # Remove the artist if we've used up all of its objects.
            if not objs_from_artist:
                del objs_by_artists[artist]

    else:
        number = min(len(objs), opts.number)
        objs = random.sample(objs, number)

    for item in objs:
        print_obj(item, lib, template)
Exemplo n.º 20
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))
Exemplo n.º 21
0
    def art_destination(self, image, item_dir=None):
        """Returns a path to the destination for the album art image
        for the album. `image` is the path of the image that will be
        moved there (used for its extension).

        The path construction uses the existing path of the album's
        items, so the album must contain at least one item or
        item_dir must be provided.
        """
        image = bytestring_path(image)
        item_dir = item_dir or self.item_dir()

        filename_tmpl = Template(beets.config['art_filename'].get(unicode))
        subpath = self.evaluate_template(filename_tmpl, True)
        subpath = util.sanitize_path(subpath,
                                     replacements=self._db.replacements)
        subpath = bytestring_path(subpath)

        _, ext = os.path.splitext(image)
        dest = os.path.join(item_dir, subpath + ext)

        return bytestring_path(dest)
Exemplo n.º 22
0
    def singletons(self, lib, query, move, pretend, write, fmt):
        """Retrieve and apply info from the autotagger for items matched by
        query.
        """
        template = Template(ui._pick_format(False, fmt))

        for item in lib.items(query + ['singleton:true']):
            item_formatted = item.evaluate_template(template)
            if not item.mb_trackid:
                self._log.info(u'Skipping singleton with no mb_trackid: {0}',
                               item_formatted)
                continue

            # Get the MusicBrainz recording info.
            track_info = hooks.track_for_mbid(item.mb_trackid)
            if not track_info:
                self._log.info(u'Recording ID not found: {0} for track {0}',
                               item.mb_trackid, item_formatted)
                continue

            # Apply.
            with lib.transaction():
                autotag.apply_item_metadata(item, track_info)
                apply_item_changes(lib, item, move, pretend, write)
Exemplo n.º 23
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>'
DEFAULT_LIST_FORMAT_ITEM = '$artist - $album - $title'
DEFAULT_LIST_FORMAT_ALBUM = '$albumartist - $album'


# UI exception. Commands should throw this in order to display
# nonrecoverable errors to the user.
class UserError(Exception):
Exemplo n.º 24
0
    def albums(self, lib, query, move, pretend, write, fmt):
        """Retrieve and apply info from the autotagger for albums matched by
        query and their items.
        """
        template = Template(ui._pick_format(True, fmt))

        # Process matching albums.
        for a in lib.albums(query):
            album_formatted = a.evaluate_template(template)
            if not a.mb_albumid:
                self._log.info(u'Skipping album with no mb_albumid: {0}',
                               album_formatted)
                continue

            items = list(a.items())

            # Get the MusicBrainz album information.
            album_info = hooks.album_for_mbid(a.mb_albumid)
            if not album_info:
                self._log.info(u'Release ID {0} not found for album {1}',
                               a.mb_albumid, album_formatted)
                continue

            # Map recording MBIDs to their information. Recordings can appear
            # multiple times on a release, so each MBID maps to a list of
            # TrackInfo objects.
            track_index = defaultdict(list)
            for track_info in album_info.tracks:
                track_index[track_info.track_id].append(track_info)

            # Construct a track mapping according to MBIDs. This should work
            # for albums that have missing or extra tracks. If there are
            # multiple copies of a recording, they are disambiguated using
            # their disc and track number.
            mapping = {}
            for item in items:
                candidates = track_index[item.mb_trackid]
                if len(candidates) == 1:
                    mapping[item] = candidates[0]
                else:
                    for c in candidates:
                        if (c.medium_index == item.track
                                and c.medium == item.disc):
                            mapping[item] = c
                            break

            # Apply.
            with lib.transaction():
                autotag.apply_metadata(album_info, mapping)
                changed = False
                for item in items:
                    item_changed = ui.show_model_changes(item)
                    changed |= item_changed
                    if item_changed:
                        apply_item_changes(lib, item, move, pretend, write)

                if not changed:
                    # No change to any item.
                    continue

                if not pretend:
                    # Update album structure to reflect an item in it.
                    for key in library.Album.item_keys:
                        a[key] = items[0][key]
                    a.store()

                    # Move album art (and any inconsistent items).
                    if move and lib.directory in util.ancestry(items[0].path):
                        self._log.debug(u'moving album {0}', album_formatted)
                        a.move()
Exemplo n.º 25
0
    def destination(self,
                    fragment=False,
                    basedir=None,
                    platform=None,
                    path_formats=None):
        """Returns the path in the library directory designated for the
        item (i.e., where the file ought to be). 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.
        """
        self._check_db()
        platform = platform or sys.platform
        basedir = basedir or self._db.directory
        path_formats = path_formats or self._db.path_formats

        # Use a path format based on a query, falling back on the
        # default.
        for query, path_format in path_formats:
            if query == PF_KEY_DEFAULT:
                continue
            query, _ = parse_query_string(query, type(self))
            if query.match(self):
                # The query matches the item! Use the corresponding path
                # format.
                break
        else:
            # No query matched; fall back to default.
            for query, path_format in path_formats:
                if query == PF_KEY_DEFAULT:
                    break
            else:
                assert False, u"no default path format"
        if isinstance(path_format, Template):
            subpath_tmpl = path_format
        else:
            subpath_tmpl = Template(path_format)

        # Evaluate the selected template.
        subpath = self.evaluate_template(subpath_tmpl, True)

        # Prepare path for output: normalize Unicode characters.
        if platform == 'darwin':
            subpath = unicodedata.normalize('NFD', subpath)
        else:
            subpath = unicodedata.normalize('NFC', subpath)

        if beets.config['asciify_paths']:
            subpath = unidecode(subpath)

        maxlen = beets.config['max_filename_length'].get(int)
        if not maxlen:
            # When zero, try to determine from filesystem.
            maxlen = util.max_filename_length(self._db.directory)

        subpath, fellback = util.legalize_path(subpath, self._db.replacements,
                                               maxlen,
                                               os.path.splitext(self.path)[1],
                                               fragment)
        if fellback:
            # Print an error message if legalization fell back to
            # default replacements because of the maximum length.
            log.warning(
                u'Fell back to default replacements when naming '
                u'file {}. Configure replacements to avoid lengthening '
                u'the filename.', subpath)

        if fragment:
            return subpath
        else:
            return normpath(os.path.join(basedir, subpath))
Exemplo n.º 26
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

Exemplo n.º 27
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 the album type, if available.
        if not item.album_id and not in_album:
            # Singleton track. Never use the "album" formats.
            if 'singleton' in self.path_formats:
                path_format = self.path_formats['singleton']
            else:
                path_format = self.path_formats['default']
        elif item.albumtype and item.albumtype in self.path_formats:
            path_format = self.path_formats[item.albumtype]
        elif item.comp and 'comp' in self.path_formats:
            path_format = self.path_formats['comp']
        else:
            path_format = self.path_formats['default']
        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']

        # Perform substitution.
        subpath = subpath_tmpl.substitute(mapping, TEMPLATE_FUNCTIONS)

        # 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)

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

        if fragment:
            return subpath
        else:
            basedir = basedir or self.directory
            return normpath(os.path.join(basedir, subpath))
Exemplo n.º 28
0
def _raw_main(args, load_config=True):
    """A helper function for `main` without top-level exception
    handling.
    """
    # Load global configuration files.
    if load_config:
        config.read()

    # Temporary: Migrate from 1.0-style configuration.
    from beets.ui import migrate
    if load_config:
        migrate.automigrate()

    # Get the default subcommands.
    from beets.ui.commands import default_commands

    # Add plugin paths.
    sys.path += get_plugin_paths()
    # Load requested plugins.
    plugins.load_plugins(config['plugins'].as_str_seq())
    plugins.send("pluginload")

    # Construct the root parser.
    commands = list(default_commands)
    commands += plugins.commands()
    commands.append(migrate.migrate_cmd)  # Temporary.
    parser = SubcommandsOptionParser(subcommands=commands)
    parser.add_option('-l',
                      '--library',
                      dest='library',
                      help='library database file to use')
    parser.add_option('-d',
                      '--directory',
                      dest='directory',
                      help="destination music directory")
    parser.add_option('-v',
                      '--verbose',
                      dest='verbose',
                      action='store_true',
                      help='print debugging information')

    # Parse the command-line!
    options, subcommand, suboptions, subargs = parser.parse_args(args)
    config.set_args(options)

    # Open library file.
    dbpath = config['library'].as_filename()
    try:
        lib = library.Library(
            dbpath,
            config['directory'].as_filename(),
            get_path_formats(),
            Template(config['art_filename'].get(unicode)),
            config['timeout'].as_number(),
            get_replacements(),
        )
    except sqlite3.OperationalError:
        raise UserError(u"database file {0} could not be opened".format(
            util.displayable_path(dbpath)))
    plugins.send("library_opened", lib=lib)

    # Configure the logger.
    if config['verbose'].get(bool):
        log.setLevel(logging.DEBUG)
    else:
        log.setLevel(logging.INFO)
    log.debug(u'data directory: {0}\n'
              u'library database: {1}\n'
              u'library directory: {2}'.format(
                  util.displayable_path(config.config_dir()),
                  util.displayable_path(lib.path),
                  util.displayable_path(lib.directory),
              ))

    # Configure the MusicBrainz API.
    mb.configure()

    # Invoke the subcommand.
    subcommand.func(lib, suboptions, subargs)
    plugins.send('cli_exit', lib=lib)
Exemplo n.º 29
0
    def destination(self, fragment=False, basedir=None, platform=None,
                    path_formats=None):
        """Returns the path in the library directory designated for the
        item (i.e., where the file ought to be). 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.
        """
        self._check_db()
        platform = platform or sys.platform
        basedir = basedir or self._db.directory
        path_formats = path_formats or self._db.path_formats

        # Use a path format based on a query, falling back on the
        # default.
        for query, path_format in path_formats:
            if query == PF_KEY_DEFAULT:
                continue
            query = get_query(query, type(self))
            if query.match(self):
                # The query matches the item! Use the corresponding path
                # format.
                break
        else:
            # No query matched; fall back to default.
            for query, path_format in path_formats:
                if query == PF_KEY_DEFAULT:
                    break
            else:
                assert False, "no default path format"
        if isinstance(path_format, Template):
            subpath_tmpl = path_format
        else:
            subpath_tmpl = Template(path_format)

        # Evaluate the selected template.
        subpath = self.evaluate_template(subpath_tmpl, True)

        # Prepare path for output: normalize Unicode characters.
        if platform == 'darwin':
            subpath = unicodedata.normalize('NFD', subpath)
        else:
            subpath = unicodedata.normalize('NFC', subpath)
        # Truncate components and remove forbidden characters.
        subpath = util.sanitize_path(subpath, self._db.replacements)
        # Encode for the filesystem.
        if not fragment:
            subpath = bytestring_path(subpath)

        # Preserve extension.
        _, extension = os.path.splitext(self.path)
        if fragment:
            # Outputting Unicode.
            extension = extension.decode('utf8', 'ignore')
        subpath += extension.lower()

        # Truncate too-long components.
        maxlen = beets.config['max_filename_length'].get(int)
        if not maxlen:
            # When zero, try to determine from filesystem.
            maxlen = util.max_filename_length(self._db.directory)
        subpath = util.truncate_path(subpath, maxlen)

        if fragment:
            return subpath
        else:
            return normpath(os.path.join(basedir, subpath))
Exemplo n.º 30
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']
     
     # Perform substitution.
     mapping.update(plugins.template_values(item))
     funcs = dict(TEMPLATE_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
     
     if fragment:
         return subpath
     else:
         basedir = basedir or self.directory
         return normpath(os.path.join(basedir, subpath))   
Exemplo n.º 31
0
import os.path
import imghdr
import subprocess
import platform
from tempfile import NamedTemporaryFile

from beets.plugins import BeetsPlugin
from beets import mediafile
from beets import ui
from beets.ui import decargs
from beets.util import syspath, normpath, displayable_path
from beets.util.artresizer import ArtResizer
from beets import config
from beets.util.functemplate import Template

__item_template = Template(ui._pick_format(False))
fmt_item = lambda item: item.evaluate_template(__item_template)
__album_template = Template(ui._pick_format(True))
fmt_album = lambda item: item.evaluate_template(__album_template)


class EmbedCoverArtPlugin(BeetsPlugin):
    """Allows albumart to be embedded into the actual files.
    """
    def __init__(self):
        super(EmbedCoverArtPlugin, self).__init__()
        self.config.add({
            'maxwidth': 0,
            'auto': True,
            'compare_threshold': 0,
            'ifempty': False,
Exemplo n.º 32
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 the album type, if available.
     if not item.album_id and not in_album:
         # Singleton track. Never use the "album" formats.
         if 'singleton' in self.path_formats:
             path_format = self.path_formats['singleton']
         else:
             path_format = self.path_formats['default']
     elif item.albumtype and item.albumtype in self.path_formats:
         path_format = self.path_formats[item.albumtype]
     elif item.comp and 'comp' in self.path_formats:
         path_format = self.path_formats['comp']
     else:
         path_format = self.path_formats['default']
     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']
     
     # Perform substitution.
     subpath = subpath_tmpl.substitute(mapping, TEMPLATE_FUNCTIONS)
     
     # 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)
     
     # Preserve extension.
     _, extension = pathmod.splitext(item.path)
     subpath += extension
     
     if fragment:
         return subpath
     else:
         basedir = basedir or self.directory
         return normpath(os.path.join(basedir, subpath))   
Exemplo n.º 33
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']
     
     # Perform substitution.
     mapping.update(plugins.template_values(item))
     funcs = dict(TEMPLATE_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
     
     if fragment:
         return subpath
     else:
         basedir = basedir or self.directory
         return normpath(os.path.join(basedir, subpath))