Example #1
0
 def _process_item(self,
                   item,
                   copy=False,
                   move=False,
                   delete=False,
                   tag=False,
                   fmt=''):
     """Process Item `item`.
     """
     print_(format(item, fmt))
     if copy:
         item.move(basedir=copy, operation=MoveOperation.COPY)
         item.store()
     if move:
         item.move(basedir=move)
         item.store()
     if delete:
         item.remove(delete=True)
     if tag:
         try:
             k, v = tag.split('=')
         except Exception:
             raise UserError(f"{PLUGIN}: can't parse k=v tag: {tag}")
         setattr(item, k, v)
         item.store()
Example #2
0
def similar(lib, src_item, threshold=0.15, fmt='${difference}: ${path}'):
    for item in lib.items():
        if item.path != src_item.path:
            d = diff(item, src_item)
            if d < threshold:
                s = fmt.replace('${difference}', '{:2.2f}'.format(d))
                ui.print_(format(item, s))
Example #3
0
        def _miss(lib, opts, args):
            self.config.set_args(opts)
            fmt = self.config['format'].get()
            count = self.config['count'].get()
            total = self.config['total'].get()

            albums = lib.albums(decargs(args))
            if total:
                print(sum([_missing_count(a) for a in albums]))
                return

            # Default format string for count mode.
            if count and not fmt:
                fmt = '$albumartist - $album: $missing'
                print fmt

            for album in albums:
                if count:
                    if _missing_count(album):
                        print_(format(album, fmt))

                else:
                    for (is_found, item) in _missing(album):
                        if is_found: sys.stdout.write('+ ')
                        else: sys.stdout.write('- ')
                        print_obj(item, lib)
Example #4
0
def show_stats(lib, query):
    """Shows some statistics about the matched items."""
    items = lib.items(query)

    total_size = 0
    total_time = 0.0
    total_items = 0
    artists = set()
    albums = set()

    for item in items:
        #fixme This is approximate, so people might complain that
        # this total size doesn't match "du -sh". Could fix this
        # by putting total file size in the database.
        total_size += int(item.length * item.bitrate / 8)
        total_time += item.length
        total_items += 1
        artists.add(item.artist)
        albums.add(item.album)

    print_("""Tracks: %i
Total time: %s
Total size: %s
Artists: %i
Albums: %i""" % (
        total_items,
        ui.human_seconds(total_time),
        ui.human_bytes(total_size),
        len(artists), len(albums)
    ))
Example #5
0
 def _process_item(self,
                   item,
                   copy=False,
                   move=False,
                   delete=False,
                   tag=False,
                   fmt=u''):
     """Process Item `item`.
     """
     print_(format(item, fmt))
     if copy:
         item.move(basedir=copy, copy=True)
         item.store()
     if move:
         item.move(basedir=move, copy=False)
         item.store()
     if delete:
         item.remove(delete=True)
     if tag:
         try:
             k, v = tag.split('=')
         except Exception:
             raise UserError(u"{}: can't parse k=v tag: {}".format(
                 PLUGIN, tag))
         setattr(item, k, v)
         item.store()
Example #6
0
def info(paths):
    # Set up fields to output.
    fields = []
    for name, _, _, mffield in library.ITEM_FIELDS:
        if mffield:
            fields.append(name)

    # Line format.
    other_fields = ['album art']
    maxwidth = max(len(name) for name in fields + other_fields)
    lineformat = u'{{0:>{0}}}: {{1}}'.format(maxwidth)

    first = True
    for path in paths:
        if not first:
            ui.print_()

        path = util.normpath(path)
        ui.print_(path)
        try:
            mf = mediafile.MediaFile(path)
        except mediafile.UnreadableFileError:
            ui.print_('cannot read file')
            continue

        # Basic fields.
        for name in fields:
            ui.print_(lineformat.format(name, getattr(mf, name)))
        # Extra stuff.
        ui.print_(lineformat.format('album art', mf.art is not None))

        first = False
Example #7
0
def choose_item(task, config):
    """Ask the user for a choice about tagging a single item. Returns
    either an action constant or a track info dictionary.
    """
    print_()
    print_(task.item.path)
    candidates, rec = task.item_match

    if config.quiet:
        # Quiet mode; make a decision.
        if task.rec == autotag.RECOMMEND_STRONG:
            dist, track_info = candidates[0]
            show_item_change(task.item.color)
            return track_info
        else:
            return _quiet_fall_back(config)

    while True:
        # Ask for a choice.
        choice = choose_candidate(candidates, True, rec, config.color,
                                  config.interactive_autotag, item=task.item)

        if choice in (importer.action.SKIP, importer.action.ASIS):
            return choice
        elif choice == importer.action.TRACKS:
            assert False # TRACKS is only legal for albums.
        elif choice == importer.action.MANUAL:
            # Continue in the loop with a new set of candidates.
            search_artist, search_title = manual_search(False)
            candidates, rec = autotag.tag_item(task.item, search_artist,
                                               search_title)
        else:
            # Chose a candidate.
            assert not isinstance(choice, importer.action)
            return choice
Example #8
0
 def album_func(self, albums, opts):
   tagged_item_count = 0
   tagged_album_count = 0
   for album in albums:
     self.albumartist = None
     self.should_set = opts.reset
     
     if not opts.reset or not opts.quiet:
       for item in album.items():
         if 'vt_albumartist' in item:
           if self.albumartist is None:
             self.albumartist = item['vt_albumartist']
           elif self.albumartist != item['vt_albumartist']:
             self.albumartist = ""
         else:
           self.should_set = True
     else:
       self.albumartist = album.albumartist
     
     if not self.should_set:
       continue
     
     if not opts.quiet:
       fmt = u'    $albumartist - $album'
       self.process_item(album, fmt)
     for item in album.items():
       tagged_item_count += self.try_sync(item)
     tagged_album_count += 1
   ui.print_()
   ui.print_(u'Changed {} item(s) in {} album(s)'.format(tagged_item_count, tagged_album_count))
Example #9
0
 def upload(self, lib, opts, args):
     items = lib.items(ui.decargs(args))
     files = self.getpaths(items)
     self.authenticate()
     ui.print_(u'Uploading your files...')
     self.m.upload(filepaths=files)
     ui.print_(u'Your files were successfully added to library')
    def update(self, create=None):
        if not os.path.isdir(self.directory) and not self.ask_create(create):
            print_(u'Skipping creation of {0}'
                   .format(displayable_path(self.directory)))
            return

        converter = self.converter()
        for (item, action) in self.items_action():
            dest = self.destination(item)
            path = self.get_path(item)
            if action == self.MOVE:
                print_(u'>{0} -> {1}'.format(displayable_path(path),
                                             displayable_path(dest)))
                util.mkdirall(dest)
                util.move(path, dest)
                util.prune_dirs(os.path.dirname(path), root=self.directory)
                self.set_path(item, dest)
                item.store()
                item.write(path=dest)
            elif action == self.WRITE:
                print_(u'*{0}'.format(displayable_path(path)))
                item.write(path=path)
            elif action == self.ADD:
                print_(u'+{0}'.format(displayable_path(dest)))
                converter.submit(item)
            elif action == self.REMOVE:
                print_(u'-{0}'.format(displayable_path(path)))
                self.remove_item(item)
                item.store()

        for item, dest in converter.as_completed():
            self.set_path(item, dest)
            item.store()
        converter.shutdown()
Example #11
0
    def album_imported(self, lib, album):
        if self.on_import == False :
            pass

        print_("Tagging Lyrics:  %s - %s" % (album.albumartist, album.album))

        def fetch(item, artist, title):
            try:
                #print_("    -%s:" % (title), ui.colorize('yellow', 'Fetching'))
                lyrics = self.fetchLyrics(scrub(artist), scrub(title))
                return (item, lyrics)
            except:
                return None
                
        def tag( item, lyrics):
            try:
                #print_("    -%s:" % (item.title), ui.colorize('green', 'Updated!'))
                item.lyrics = lyrics
                item.write()
                lib.store(item)
            except:
                pass
                
        [(item, item.artist, item.title) for item in album.items()]  \
            >> ThreadPool(apply(fetch), poolsize=self.processcount)  \
            >> filter( lambda itm: itm != None) \
            >> ThreadPool(apply(tag), poolsize=1)
Example #12
0
def _summary_judment(rec):
    """Determines whether a decision should be made without even asking
    the user. This occurs in quiet mode and when an action is chosen for
    NONE recommendations. Return an action or None if the user should be
    queried. May also print to the console if a summary judgment is
    made.
    """
    if config['import']['quiet']:
        if rec == recommendation.strong:
            return importer.action.APPLY
        else:
            action = config['import']['quiet_fallback'].as_choice({
                'skip': importer.action.SKIP,
                'asis': importer.action.ASIS,
            })

    elif rec == recommendation.none:
        action = config['import']['none_rec_action'].as_choice({
            'skip': importer.action.SKIP,
            'asis': importer.action.ASIS,
            'ask': None,
        })

    else:
        return None

    if action == importer.action.SKIP:
        print_('Skipping.')
    elif action == importer.action.ASIS:
        print_('Importing as-is.')
    return action
Example #13
0
    def command(self, lib, opts, args):
        self.config.set_args(opts)
        self.set_fields()

        query = decargs(args)
        for album in lib.albums(query):
            inconsistent_fields = defaultdict(list)
            album_items = album.items()

            for item in album_items:
                for field in self.included_fields:
                    if item[field] != album[field]:
                        inconsistent_fields[field].append(item)

            for field in sorted(inconsistent_fields):
                items = inconsistent_fields[field]
                if len(items) == len(album_items):
                    print_(
                        u'{}: field `{}` has album value `{}` but all track values are `{}`'
                        .format(album, field, album[field], items[0][field]))
                else:
                    for item in items:
                        print_(
                            u'{}: field `{}` has value `{}` but album value is `{}`'
                            .format(item, field, item[field], album[field]))
Example #14
0
def remove_items(lib, query, album, delete=False):
    """Remove items matching query from lib. If album, then match and
    remove whole albums. If delete, also remove files from disk.
    """
    # Get the matching items.
    items, albums = _do_query(lib, query, album)

    # Show all the items.
    for item in items:
        print_(item.artist + ' - ' + item.album + ' - ' + item.title)

    # Confirm with user.
    print_()
    if delete:
        prompt = 'Really DELETE %i files (y/n)?' % len(items)
    else:
        prompt = 'Really remove %i items from the library (y/n)?' % \
                 len(items)
    if not ui.input_yn(prompt, True):
        return

    # Remove (and possibly delete) items.
    with lib.transaction():
        if album:
            for al in albums:
                al.remove(delete)
        else:
            for item in items:
                lib.remove(item, delete)
Example #15
0
 def upload(self, lib, opts, args):
     items = lib.items(ui.decargs(args))
     files = self.getpaths(items)
     self.authenticate()
     ui.print_('Uploading your files...')
     self.m.upload(filepaths=files)
     ui.print_('Your files were successfully added to library')
Example #16
0
 def search(self, lib, opts, args):
     password = config['gmusic']['password']
     email = config['gmusic']['email']
     password.redact = True
     email.redact = True
     # Since Musicmanager doesn't support library management
     # we need to use mobileclient interface
     mobile = Mobileclient()
     try:
         mobile.login(email.as_str(), password.as_str(),
                      Mobileclient.FROM_MAC_ADDRESS)
         files = mobile.get_all_songs()
     except NotLoggedIn:
         ui.print_(
             u'Authentication error. Please check your email and password.'
         )
         return
     if not args:
         for i, file in enumerate(files, start=1):
             print(i, ui.colorize('blue', file['artist']),
                   file['title'], ui.colorize('red', file['album']))
     else:
         if opts.track:
             self.match(files, args, 'title')
         else:
             self.match(files, args, 'artist')
Example #17
0
    def preview_playlist_command(self, lib, opts, args):
        name_column_length = 60
        count = 10

        self._log.info(config.user_config_path())

        if opts.count:
            count = int(opts.count)

        if opts.playlist:
            if opts.playlist not in _settings.playlists:
                self._log.error(u'Playlist not defined: {}'.format(opts.playlist))
                return
            query = _settings.playlists[opts.playlist].query.split(u" ")
        else:
            query = decargs(args)

        query = u" ".join(query)
        query = ignore_deleted_in_query(query)
        items = playlist_generator.generate_playlist(lib, _settings.rules, count, opts.shuffle, query)

        for item in items:
            score_string = ", ".join(['%s: %s' % (key.replace("rule_", ""), value) for (key, value) in sorted(item.scores.items())])
            score_sum = round(sum(item.scores.values()), 2)
            item_name = unicode(item)
            if len(item_name) > name_column_length-5:
                item_name = item_name[:name_column_length-5] + "..."
            item_string = item_name.ljust(name_column_length)
            print_(u"Track: {0} Scores: {1}=[{2}]".format(item_string, score_sum, score_string))
Example #18
0
    def convert_func(self, lib, opts, args):
        dest = opts.dest or self.config['dest'].get()
        if not dest:
            raise ui.UserError(u'no convert destination set')
        dest = util.bytestring_path(dest)

        threads = opts.threads or self.config['threads'].get(int)

        path_formats = ui.get_path_formats(self.config['paths'] or None)

        fmt = opts.format or self.config['format'].as_str().lower()

        if opts.pretend is not None:
            pretend = opts.pretend
        else:
            pretend = self.config['pretend'].get(bool)

        if opts.hardlink is not None:
            hardlink = opts.hardlink
            link = False
        elif opts.link is not None:
            hardlink = False
            link = opts.link
        else:
            hardlink = self.config['hardlink'].get(bool)
            link = self.config['link'].get(bool)

        if opts.album:
            albums = lib.albums(ui.decargs(args))
            items = [i for a in albums for i in a.items()]
            if not pretend:
                for a in albums:
                    ui.print_(format(a, u''))
        else:
            items = list(lib.items(ui.decargs(args)))
            if not pretend:
                for i in items:
                    ui.print_(format(i, u''))

        if not items:
            self._log.error(u'Empty query result.')
            return
        if not (pretend or opts.yes or ui.input_yn(u"Convert? (Y/n)")):
            return

        if opts.album and self.config['copy_album_art']:
            for album in albums:
                self.copy_album_art(album, dest, path_formats, pretend,
                                    link, hardlink)

        convert = [self.convert_item(dest,
                                     opts.keep_new,
                                     path_formats,
                                     fmt,
                                     pretend,
                                     link,
                                     hardlink)
                   for _ in range(threads)]
        pipe = util.pipeline.Pipeline([iter(items), convert])
        pipe.run_parallel()
Example #19
0
def _process_item(item,
                  lib,
                  copy=False,
                  move=False,
                  delete=False,
                  tag=False,
                  format=''):
    """Process Item `item` in `lib`.
    """
    if copy:
        item.move(basedir=copy, copy=True)
        item.store()
    if move:
        item.move(basedir=move, copy=False)
        item.store()
    if delete:
        item.remove(delete=True)
    if tag:
        try:
            k, v = tag.split('=')
        except:
            raise UserError('%s: can\'t parse k=v tag: %s' % (PLUGIN, tag))
        setattr(k, v)
        item.store()
    print_(format(item, format))
Example #20
0
    def update(self, create=None):
        if not os.path.isdir(self.directory) and not self.ask_create(create):
            print_(u'Skipping creation of {0}'.format(
                displayable_path(self.directory)))
            return

        converter = self.converter()
        for (item, action) in self.items_action():
            dest = self.destination(item)
            path = self.get_path(item)
            if action == self.MOVE:
                print_(u'>{0} -> {1}'.format(displayable_path(path),
                                             displayable_path(dest)))
                util.mkdirall(dest)
                util.move(path, dest)
                util.prune_dirs(os.path.dirname(path), root=self.directory)
                self.set_path(item, dest)
                item.store()
                item.write(path=dest)
            elif action == self.WRITE:
                print_(u'*{0}'.format(displayable_path(path)))
                item.write(path=path)
            elif action == self.ADD:
                print_(u'+{0}'.format(displayable_path(dest)))
                converter.submit(item)
            elif action == self.REMOVE:
                print_(u'-{0}'.format(displayable_path(path)))
                self.remove_item(item)
                item.store()

        for item, dest in converter.as_completed():
            self.set_path(item, dest)
            item.store()
        converter.shutdown()
Example #21
0
def remove_items(lib, query, album, delete):
    """Remove items matching query from lib. If album, then match and
    remove whole albums. If delete, also remove files from disk.
    """
    # Get the matching items.
    items, albums = _do_query(lib, query, album)

    # Show all the items.
    for item in items:
        ui.print_obj(item, lib)

    # Confirm with user.
    print_()
    if delete:
        prompt = 'Really DELETE %i files (y/n)?' % len(items)
    else:
        prompt = 'Really remove %i items from the library (y/n)?' % \
                 len(items)
    if not ui.input_yn(prompt, True):
        return

    # Remove (and possibly delete) items.
    with lib.transaction():
        for obj in (albums if album else items):
            obj.remove(delete)
Example #22
0
def remove_items(lib, query, album, delete=False):
    """Remove items matching query from lib. If album, then match and
    remove whole albums. If delete, also remove files from disk.
    """
    # Get the matching items.
    items, albums = _do_query(lib, query, album)

    # Show all the items.
    for item in items:
        print_(item.artist + ' - ' + item.album + ' - ' + item.title)

    # Confirm with user.
    print_()
    if delete:
        prompt = 'Really DELETE %i files (y/n)?' % len(items)
    else:
        prompt = 'Really remove %i items from the library (y/n)?' % \
                 len(items)
    if not ui.input_yn(prompt, True):
        return

    # Remove (and possibly delete) items.
    if album:
        for al in albums:
            al.remove(delete)
    else:
        for item in items:
            lib.remove(item, delete)

    lib.save()
Example #23
0
def _summary_judment(rec):
    """Determines whether a decision should be made without even asking
    the user. This occurs in quiet mode and when an action is chosen for
    NONE recommendations. Return an action or None if the user should be
    queried. May also print to the console if a summary judgment is
    made.
    """
    if config['import']['quiet']:
        if rec == recommendation.strong:
            return importer.action.APPLY
        else:
            action = config['import']['quiet_fallback'].as_choice({
                'skip': importer.action.SKIP,
                'asis': importer.action.ASIS,
            })

    elif rec == recommendation.none:
        action = config['import']['none_rec_action'].as_choice({
            'skip': importer.action.SKIP,
            'asis': importer.action.ASIS,
            'ask': None,
        })

    else:
        return None

    if action == importer.action.SKIP:
        print_('Skipping.')
    elif action == importer.action.ASIS:
        print_('Importing as-is.')
    return action
Example #24
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 = []
        items.extend(_items_for_query(lib, playlist, True))
        items.extend(_items_for_query(lib, playlist, False))

        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(basename, True)
            if not (m3u_name in m3us):
                m3us[m3u_name] = []
            item_path = item.path
            if relative_to:
                item_path = os.path.relpath(item.path, relative_to)
            if item_path not in m3us[m3u_name]:
                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")
Example #25
0
def show_stats(lib, query):
    """Shows some statistics about the matched items."""
    items = lib.items(query)

    total_size = 0
    total_time = 0.0
    total_items = 0
    artists = set()
    albums = set()

    for item in items:
        #fixme This is approximate, so people might complain that
        # this total size doesn't match "du -sh". Could fix this
        # by putting total file size in the database.
        total_size += int(item.length * item.bitrate / 8)
        total_time += item.length
        total_items += 1
        artists.add(item.artist)
        albums.add(item.album)

    print_("""Tracks: %i
Total time: %s
Total size: %s
Artists: %i
Albums: %i""" % (total_items, ui.human_seconds(total_time),
                 ui.human_bytes(total_size), len(artists), len(albums)))
Example #26
0
    def _missing_tracks(self, lib, query):
        """Print a listing of tracks missing from each album in the library
        matching query.
        """
        albums = lib.albums(query)

        count = self.config['count'].get()
        total = self.config['total'].get()
        fmt = config['format_album' if count else 'format_item'].get()

        if total:
            print(sum([_missing_count(a) for a in albums]))
            return

        # Default format string for count mode.
        if count:
            fmt += ': $missing'

        for album in albums:
            if count:
                if _missing_count(album):
                    print_(format(album, fmt))

            else:
                for item in self._missing(album):
                    print_(format(item, fmt))
Example #27
0
def modify_items(lib, mods, query, write, move, album, confirm):
    """Modifies matching items according to key=value assignments."""
    # Parse key=value specifications into a dictionary.
    if album:
        allowed_keys = library.ALBUM_KEYS
    else:
        allowed_keys = library.ITEM_KEYS_WRITABLE + ['added']
    fsets = {}
    for mod in mods:
        key, value = mod.split('=', 1)
        if key not in allowed_keys:
            raise ui.UserError('"%s" is not a valid field' % key)
        fsets[key] = _convert_type(key, value, album)

    # Get the items to modify.
    items, albums = _do_query(lib, query, album, False)
    objs = albums if album else items

    # Preview change.
    print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item'))
    for obj in objs:
        # Identify the changed object.
        ui.print_obj(obj, lib)

        # Show each change.
        for field, value in fsets.iteritems():
            curval = getattr(obj, field)
            _showdiff(field, curval, value)

    # Confirm.
    if confirm:
        extra = ' and write tags' if write else ''
        if not ui.input_yn('Really modify%s (Y/n)?' % extra):
            return

    # Apply changes to database.
    with lib.transaction():
        for obj in objs:
            for field, value in fsets.iteritems():
                setattr(obj, field, value)

            if move:
                cur_path = obj.item_dir() if album else obj.path
                if lib.directory in ancestry(cur_path): # In library?
                    log.debug('moving object %s' % cur_path)
                    if album:
                        obj.move()
                    else:
                        lib.move(obj)

            # When modifying items, we have to store them to the database.
            if not album:
                lib.store(obj)

    # Apply tags if requested.
    if write:
        if album:
            items = itertools.chain(*(a.items() for a in albums))
        for item in items:
            item.write()
Example #28
0
def show_stats(lib, query, exact):
    """Shows some statistics about the matched items."""
    items = lib.items(query)

    total_size = 0
    total_time = 0.0
    total_items = 0
    artists = set()
    albums = set()

    for item in items:
        if exact:
            total_size += os.path.getsize(item.path)
        else:
            total_size += int(item.length * item.bitrate / 8)
        total_time += item.length
        total_items += 1
        artists.add(item.artist)
        albums.add(item.album)

    size_str = '' + ui.human_bytes(total_size)
    if exact:
        size_str += ' ({0} bytes)'.format(total_size)

    print_("""Tracks: {0}
Total time: {1} ({2:.2f} seconds)
Total size: {3}
Artists: {4}
Albums: {5}""".format(total_items, ui.human_seconds(total_time), total_time,
                      size_str, len(artists), len(albums)))
Example #29
0
def modify_items(lib, mods, query, write, move, album, confirm):
    """Modifies matching items according to key=value assignments."""
    # Parse key=value specifications into a dictionary.
    if album:
        allowed_keys = library.ALBUM_KEYS
    else:
        allowed_keys = library.ITEM_KEYS_WRITABLE + ['added']
    fsets = {}
    for mod in mods:
        key, value = mod.split('=', 1)
        if key not in allowed_keys:
            raise ui.UserError('"%s" is not a valid field' % key)
        fsets[key] = _convert_type(key, value, album)

    # Get the items to modify.
    items, albums = _do_query(lib, query, album, False)
    objs = albums if album else items

    # Preview change.
    print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item'))
    for obj in objs:
        # Identify the changed object.
        ui.print_obj(obj, lib)

        # Show each change.
        for field, value in fsets.iteritems():
            curval = getattr(obj, field)
            _showdiff(field, curval, value)

    # Confirm.
    if confirm:
        extra = ' and write tags' if write else ''
        if not ui.input_yn('Really modify%s (Y/n)?' % extra):
            return

    # Apply changes to database.
    with lib.transaction():
        for obj in objs:
            for field, value in fsets.iteritems():
                setattr(obj, field, value)

            if move:
                cur_path = obj.item_dir() if album else obj.path
                if lib.directory in ancestry(cur_path):  # In library?
                    log.debug('moving object %s' % cur_path)
                    if album:
                        obj.move()
                    else:
                        lib.move(obj)

            # When modifying items, we have to store them to the database.
            if not album:
                lib.store(obj)

    # Apply tags if requested.
    if write:
        if album:
            items = itertools.chain(*(a.items() for a in albums))
        for item in items:
            item.write()
Example #30
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 = []
        items.extend(_items_for_query(lib, playlist, True))
        items.extend(_items_for_query(lib, playlist, False))

        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(basename, True)
            if not (m3u_name in m3us):
                m3us[m3u_name] = []
            item_path = item.path
            if relative_to:
                item_path = os.path.relpath(item.path, relative_to)
            if item_path not in m3us[m3u_name]:
                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")
Example #31
0
 def search(self, lib, opts, args):
     password = config['gmusic']['password']
     email = config['gmusic']['email']
     password.redact = True
     email.redact = True
     # Since Musicmanager doesn't support library management
     # we need to use mobileclient interface
     mobile = Mobileclient()
     try:
         mobile.login(email.as_str(), password.as_str(),
                      Mobileclient.FROM_MAC_ADDRESS)
         files = mobile.get_all_songs()
     except NotLoggedIn:
         ui.print_(
             u'Authentication error. Please check your email and password.')
         return
     if not args:
         for i, file in enumerate(files, start=1):
             print(i, ui.colorize('blue', file['artist']), file['title'],
                   ui.colorize('red', file['album']))
     else:
         if opts.track:
             self.match(files, args, 'title')
         else:
             self.match(files, args, 'artist')
Example #32
0
def remove_items(lib, query, album, delete):
    """Remove items matching query from lib. If album, then match and
    remove whole albums. If delete, also remove files from disk.
    """
    # Get the matching items.
    items, albums = _do_query(lib, query, album)

    # Show all the items.
    for item in items:
        ui.print_obj(item, lib)

    # Confirm with user.
    print_()
    if delete:
        prompt = 'Really DELETE %i files (y/n)?' % len(items)
    else:
        prompt = 'Really remove %i items from the library (y/n)?' % \
                 len(items)
    if not ui.input_yn(prompt, True):
        return

    # Remove (and possibly delete) items.
    with lib.transaction():
        for obj in (albums if album else items):
            obj.remove(delete)
Example #33
0
    def open(self, lib, opts, args):
        query = ui.decargs(args)

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

        if not items:
            raise ui.UserError("nothing to open")

        cmd = util.open_anything()
        if opts.args:
            cmd += " " + opts.args
        paths = [item.path for item in items]
        if opts.reveal:
            paths = [os.path.dirname(p) for p in paths]
        self._log.debug("invoking command: {} {}", cmd,
                        subprocess.list2cmdline(paths))

        item_type = "album" if opts.album else "track"
        item_type += "s" if len(items) > 1 else ""
        if opts.reveal:
            action = "Revealing"
        else:
            action = "Opening"
        ui.print_("{} {} {}.".format(action, len(items), item_type))

        try:
            util.interactive_open(paths, cmd)
        except OSError as exc:
            raise ui.UserError("failed to invoke {}: {}".format(cmd, exc))
Example #34
0
File: info.py Project: nidico/beets
def info(paths):
    # Set up fields to output.
    fields = []
    for name, _, _, mffield in library.ITEM_FIELDS:
        if mffield:
            fields.append(name)

    # Line format.
    other_fields = ['album art']
    maxwidth = max(len(name) for name in fields + other_fields)
    lineformat = u'{{:>{0}}}: {{0}}'.format(maxwidth)

    first = True
    for path in paths:
        if not first:
            ui.print_()

        path = util.normpath(path)
        ui.print_(path)
        try:
            mf = mediafile.MediaFile(path)
        except mediafile.UnreadableFileError:
            ui.print_('cannot read file')
            continue

        # Basic fields.
        for name in fields:
            ui.print_(lineformat.format(name, getattr(mf, name)))
        # Extra stuff.
        ui.print_(lineformat.format('album art', mf.art is not None))


        first = False
Example #35
0
    def _missing_tracks(self, lib, query):
        """Print a listing of tracks missing from each album in the library
        matching query.
        """
        albums = lib.albums(query)

        count = self.config['count'].get()
        total = self.config['total'].get()
        fmt = config['format_album' if count else 'format_item'].get()

        if total:
            print(sum([_missing_count(a) for a in albums]))
            return

        # Default format string for count mode.
        if count:
            fmt += ': $missing'

        for album in albums:
            if count:
                if _missing_count(album):
                    print_(format(album, fmt))

            else:
                for item in self._missing(album):
                    print_(format(item, fmt))
Example #36
0
def similar(lib, src_item, threshold=0.15, fmt='${difference}: ${path}'):
    for item in lib.items():
        if item.path != src_item.path:
            d = diff(item, src_item)
            if d < threshold:
                s = fmt.replace('${difference}', '{:2.2f}'.format(d))
                ui.print_(format(item, s))
Example #37
0
    def convert_func(self, lib, opts, args):
        (dest, threads, path_formats, fmt, pretend, hardlink,
         link) = self._get_opts_and_config(opts)

        if opts.album:
            albums = lib.albums(ui.decargs(args))
            items = [i for a in albums for i in a.items()]
            if not pretend:
                for a in albums:
                    ui.print_(format(a, ''))
        else:
            items = list(lib.items(ui.decargs(args)))
            if not pretend:
                for i in items:
                    ui.print_(format(i, ''))

        if not items:
            self._log.error('Empty query result.')
            return
        if not (pretend or opts.yes or ui.input_yn("Convert? (Y/n)")):
            return

        if opts.album and self.config['copy_album_art']:
            for album in albums:
                self.copy_album_art(album, dest, path_formats, pretend, link,
                                    hardlink)

        self._parallel_convert(dest, opts.keep_new, path_formats, fmt, pretend,
                               link, hardlink, threads, items)
Example #38
0
 def func(lib, opts, args):
     # The "write to files" option corresponds to the
     # import_write config value.
     write = ui.should_write()
     if opts.writerest:
         self.writerest_indexes(opts.writerest)
     for item in lib.items(ui.decargs(args)):
         if not opts.local_only and not self.config['local']:
             self.fetch_item_lyrics(
                 lib,
                 item,
                 write,
                 opts.force_refetch or self.config['force'],
             )
         if item.lyrics:
             if opts.printlyr:
                 ui.print_(item.lyrics)
             if opts.writerest:
                 self.writerest(opts.writerest, item)
     if opts.writerest:
         # flush last artist
         self.writerest(opts.writerest, None)
         ui.print_(u'ReST files generated. to build, use one of:')
         ui.print_(u'  sphinx-build -b html %s _build/html' %
                   opts.writerest)
         ui.print_(u'  sphinx-build -b epub %s _build/epub' %
                   opts.writerest)
         ui.print_(
             (u'  sphinx-build -b latex %s _build/latex '
              u'&& make -C _build/latex all-pdf') % opts.writerest)
Example #39
0
def _summary_judment(rec):
    """Determines whether a decision should be made without even asking
    the user. This occurs in quiet mode and when an action is chosen for
    NONE recommendations. Return an action or None if the user should be
    queried. May also print to the console if a summary judgment is
    made.
    """
    if config["import"]["quiet"]:
        if rec == recommendation.strong:
            return importer.action.APPLY
        else:
            action = config["import"]["quiet_fallback"].as_choice(
                {"skip": importer.action.SKIP, "asis": importer.action.ASIS}
            )

    elif rec == recommendation.none:
        action = config["import"]["none_rec_action"].as_choice(
            {"skip": importer.action.SKIP, "asis": importer.action.ASIS, "ask": None}
        )

    else:
        return None

    if action == importer.action.SKIP:
        print_("Skipping.")
    elif action == importer.action.ASIS:
        print_("Importing as-is.")
    return action
Example #40
0
 def func(lib, opts, args):
     # The "write to files" option corresponds to the
     # import_write config value.
     write = ui.should_write()
     if opts.writerest:
         self.writerest_indexes(opts.writerest)
     for item in lib.items(ui.decargs(args)):
         if not opts.local_only and not self.config['local']:
             self.fetch_item_lyrics(
                 lib, item, write,
                 opts.force_refetch or self.config['force'],
             )
         if item.lyrics:
             if opts.printlyr:
                 ui.print_(item.lyrics)
             if opts.writerest:
                 self.writerest(opts.writerest, item)
     if opts.writerest:
         # flush last artist
         self.writerest(opts.writerest, None)
         ui.print_(u'ReST files generated. to build, use one of:')
         ui.print_(u'  sphinx-build -b html %s _build/html'
                   % opts.writerest)
         ui.print_(u'  sphinx-build -b epub %s _build/epub'
                   % opts.writerest)
         ui.print_((u'  sphinx-build -b latex %s _build/latex '
                    u'&& make -C _build/latex all-pdf')
                   % opts.writerest)
Example #41
0
def show_stats(lib, query, exact):
    """Shows some statistics about the matched items."""
    items = lib.items(query)

    total_size = 0
    total_time = 0.0
    total_items = 0
    artists = set()
    albums = set()

    for item in items:
        if exact:
            total_size += os.path.getsize(item.path)
        else:
            total_size += int(item.length * item.bitrate / 8)
        total_time += item.length
        total_items += 1
        artists.add(item.artist)
        albums.add(item.album)

    size_str = '' + ui.human_bytes(total_size)
    if exact:
        size_str += ' ({0} bytes)'.format(total_size)

    print_("""Tracks: {0}
Total time: {1} ({2:.2f} seconds)
Total size: {3}
Artists: {4}
Albums: {5}""".format(total_items, ui.human_seconds(total_time), total_time,
                      size_str, len(artists), len(albums)))
Example #42
0
def modify_items(lib, mods, query, write, move, album, confirm):
    """Modifies matching items according to key=value assignments."""
    # Parse key=value specifications into a dictionary.
    model_cls = library.Album if album else library.Item
    fsets = {}
    for mod in mods:
        key, value = mod.split('=', 1)
        fsets[key] = model_cls._parse(key, value)

    # Get the items to modify.
    items, albums = _do_query(lib, query, album, False)
    objs = albums if album else items

    # Preview change and collect modified objects.
    print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item'))
    changed = set()
    for obj in objs:
        # Identify the changed object.
        ui.print_obj(obj, lib)

        # Show each change.
        for field, value in fsets.iteritems():
            if _showdiff(field, obj._get_formatted(field),
                         obj._format(field, value)):
                changed.add(obj)

    # Still something to do?
    if not changed:
        print_('No changes to make.')
        return

    # Confirm action.
    if confirm:
        extra = ' and write tags' if write else ''
        if not ui.input_yn('Really modify%s (Y/n)?' % extra):
            return

    # Apply changes to database.
    with lib.transaction():
        for obj in changed:
            for field, value in fsets.iteritems():
                obj[field] = value

            if move:
                cur_path = obj.path
                if lib.directory in ancestry(cur_path): # In library?
                    log.debug('moving object %s' % cur_path)
                    obj.move()

            obj.store()

    # Apply tags if requested.
    if write:
        if album:
            changed_items = itertools.chain(*(a.items() for a in changed))
        else:
            changed_items = changed
        for item in changed_items:
            item.write()
Example #43
0
 def func(lib, opts, args):
     # The "write to files" option corresponds to the
     # import_write config value.
     write = ui.should_write()
     for item in lib.items(ui.decargs(args)):
         self.fetch_item_lyrics(lib, item, write, opts.force_refetch or self.config["force"])
         if opts.printlyr and item.lyrics:
             ui.print_(item.lyrics)
Example #44
0
 def show_album(artist, album):
     if artist:
         album_description = u'    %s - %s' % (artist, album)
     elif album:
         album_description = u'    %s' % album
     else:
         album_description = u'    (unknown album)'
     print_(album_description)
Example #45
0
 def 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)
     for item in lib.items(ui.decargs(args)):
         fetch_item_lyrics(lib, logging.INFO, item, write)
         if opts.printlyr and item.lyrics:
             ui.print_(item.lyrics)
Example #46
0
 def func(lib, opts, args):
     # The "write to files" option corresponds to the
     # import_write config value.
     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:
             ui.print_(item.lyrics)
Example #47
0
 def show_album(artist, album):
     if artist:
         album_description = u'    %s - %s' % (artist, album)
     elif album:
         album_description = u'    %s' % album
     else:
         album_description = u'    (unknown album)'
     print_(album_description)
Example #48
0
 def func(lib, opts, args):
     # The "write to files" option corresponds to the
     # import_write config value.
     write = config['import']['write'].get(bool)
     for item in lib.items(ui.decargs(args)):
         self.fetch_item_lyrics(lib, logging.INFO, item, write)
         if opts.printlyr and item.lyrics:
             ui.print_(item.lyrics)
Example #49
0
 def read_advisory(self, lib, opts, args):
     self.config.set_args(opts)
     query = decargs(args)
     items, _ = _do_query(lib, query, None, False)
     if not items:
         print_(u'No items matched the specified query: {0}.'.format(' '.join(query)))
         return
     self.read_items(lib, items, pretend=opts.pretend)
Example #50
0
    def list_command(self, lib, opts, args):
        self.handle_common_args(opts, args)

        for item, is_sole_track in self.list_sole_tracks(lib):
            if is_sole_track:
                ui.print_(format(item))
            elif opts.non_matching and not is_sole_track:
                ui.print_(format(item))
Example #51
0
 def fetch(mf):
     try:
         print_("    -%s:" % (mf.title), ui.colorize('yellow', 'Fetching'))
         lyrics = self.fetchLyrics(scrub(mf.artist), scrub(mf.title))
         result = (mf, lyrics);
         return result
     except:
         return None
Example #52
0
 def show_album(artist, album):
     if artist:
         album_description = u"    %s - %s" % (artist, album)
     elif album:
         album_description = u"    %s" % album
     else:
         album_description = u"    (unknown album)"
     print_(album_description)
Example #53
0
def modify_items(lib, mods, dels, query, write, move, album, confirm):
    """Modifies matching items according to key=value assignments."""
    # Parse key=value specifications into a dictionary.
    model_cls = library.Album if album else library.Item
    fsets = {}
    for mod in mods:
        key, value = mod.split('=', 1)
        fsets[key] = model_cls._parse(key, value)

    # Get the items to modify.
    items, albums = _do_query(lib, query, album, False)
    objs = albums if album else items

    # Apply changes *temporarily*, preview them, and collect modified
    # objects.
    print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item'))
    changed = set()
    for obj in objs:
        for field, value in fsets.iteritems():
            obj[field] = value
        for field in dels:
            del obj[field]
        if ui.show_model_changes(obj):
            changed.add(obj)

    # Still something to do?
    if not changed:
        print_('No changes to make.')
        return

    # Confirm action.
    if confirm:
        extra = ' and write tags' if write else ''
        if not ui.input_yn('Really modify%s (Y/n)?' % extra):
            return

    # Apply changes to database.
    with lib.transaction():
        for obj in changed:
            if move:
                cur_path = obj.path
                if lib.directory in ancestry(cur_path): # In library?
                    log.debug('moving object %s' % cur_path)
                    obj.move()

            obj.store()

    # Apply tags if requested.
    if write:
        if album:
            changed_items = itertools.chain(*(a.items() for a in changed))
        else:
            changed_items = changed
        for item in changed_items:
            try:
                item.write()
            except library.FileOperationError as exc:
                log.error(exc)
Example #54
0
def update_items(lib, query, album, move, color):
    """For all the items matched by the query, update the library to
    reflect the item's embedded tags.
    """
    items, _ = _do_query(lib, query, album)

    # Walk through the items and pick up their changes.
    affected_albums = set()
    for item in items:
        # Item deleted?
        if not os.path.exists(syspath(item.path)):
            print_(u'X %s - %s' % (item.artist, item.title))
            lib.remove(item, True)
            affected_albums.add(item.album_id)
            continue

        # Read new data.
        old_data = dict(item.record)
        item.read()

        # Get and save metadata changes.
        changes = {}
        for key in library.ITEM_KEYS_META:
            if item.dirty[key]:
                changes[key] = old_data[key], getattr(item, key)
        if changes:
            # Something changed.
            print_(u'* %s - %s' % (item.artist, item.title))
            for key, (oldval, newval) in changes.iteritems():
                _showdiff(key, oldval, newval, color)

            # Move the item if it's in the library.
            if move and lib.directory in ancestry(item.path):
                item.move(lib)

            lib.store(item)
            affected_albums.add(item.album_id)

    # Modify affected albums to reflect changes in their items.
    for album_id in affected_albums:
        if album_id is None: # Singletons.
            continue
        album = lib.get_album(album_id)
        if not album: # Empty albums have already been removed.
            log.debug('emptied album %i' % album_id)
            continue
        al_items = list(album.items())

        # Update album structure to reflect an item in it.
        for key in library.ALBUM_KEYS_ITEM:
            setattr(album, key, getattr(al_items[0], key))

        # Move album art (and any inconsistent items).
        if move and lib.directory in ancestry(al_items[0].path):
            log.debug('moving album %i' % album_id)
            album.move()

    lib.save()
Example #55
0
def modify_items(lib, mods, dels, query, write, move, album, confirm):
    """Modifies matching items according to key=value assignments."""
    # Parse key=value specifications into a dictionary.
    model_cls = library.Album if album else library.Item
    fsets = {}
    for mod in mods:
        key, value = mod.split('=', 1)
        fsets[key] = model_cls._parse(key, value)

    # Get the items to modify.
    items, albums = _do_query(lib, query, album, False)
    objs = albums if album else items

    # Apply changes *temporarily*, preview them, and collect modified
    # objects.
    print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item'))
    changed = set()
    for obj in objs:
        for field, value in fsets.iteritems():
            obj[field] = value
        for field in dels:
            del obj[field]
        if ui.show_model_changes(obj):
            changed.add(obj)

    # Still something to do?
    if not changed:
        print_('No changes to make.')
        return

    # Confirm action.
    if confirm:
        extra = ' and write tags' if write else ''
        if not ui.input_yn('Really modify%s (Y/n)?' % extra):
            return

    # Apply changes to database.
    with lib.transaction():
        for obj in changed:
            if move:
                cur_path = obj.path
                if lib.directory in ancestry(cur_path): # In library?
                    log.debug('moving object %s' % cur_path)
                    obj.move()

            obj.store()

    # Apply tags if requested.
    if write:
        if album:
            changed_items = itertools.chain(*(a.items() for a in changed))
        else:
            changed_items = changed
        for item in changed_items:
            try:
                item.write()
            except library.FileOperationError as exc:
                log.error(exc)
Example #56
0
def choose_match(task, config):
    """Given an initial autotagging of items, go through an interactive
    dance with the user to ask for a choice of metadata. Returns an
    (info, items) pair, ASIS, or SKIP.
    """
    # Show what we're tagging.
    print_()
    print_(task.path)

    if config.quiet:
        # No input; just make a decision.
        if task.rec == autotag.RECOMMEND_STRONG:
            dist, items, info = task.candidates[0]
            show_change(task.cur_artist, task.cur_album, items, info, dist, config.color)
            return info, items
        else:
            return _quiet_fall_back(config)

    # Loop until we have a choice.
    candidates, rec = task.candidates, task.rec
    while True:
        # Ask for a choice from the user.
        choice = choose_candidate(
            candidates,
            False,
            rec,
            config.color,
            config.timid,
            task.cur_artist,
            task.cur_album,
            itemcount=len(task.items),
            per_disc_numbering=config.per_disc_numbering,
        )

        # Choose which tags to use.
        if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS):
            # Pass selection to main control flow.
            return choice
        elif choice is importer.action.MANUAL:
            # Try again with manual search terms.
            search_artist, search_album = manual_search(False)
            try:
                _, _, candidates, rec = autotag.tag_album(task.items, config.timid, search_artist, search_album)
            except autotag.AutotagError:
                candidates, rec = None, None
        elif choice is importer.action.MANUAL_ID:
            # Try a manually-entered ID.
            search_id = manual_id(False)
            if search_id:
                try:
                    _, _, candidates, rec = autotag.tag_album(task.items, config.timid, search_id=search_id)
                except autotag.AutotagError:
                    candidates, rec = None, None
        else:
            # We have a candidate! Finish tagging. Here, choice is
            # an (info, items) pair as desired.
            assert not isinstance(choice, importer.action)
            return choice
Example #57
0
 def 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)
     for item in lib.items(ui.decargs(args)):
         fetch_item_lyrics(lib, logging.INFO, item, write)
         if opts.printlyr and item.lyrics:
             ui.print_(item.lyrics)
Example #58
0
    def modify_items(self, lib, mods, dels, query, write, move, album,
                     confirm):
        """Modifies matching items according to user-specified assignments and
        deletions.

        `mods` is a dictionary of field and value pairse indicating
        assignments. `dels` is a list of fields to be deleted.
        """
        # Parse key=value specifications into a dictionary.
        model_cls = library.Album if album else library.Item

        # Get the items to modify.
        items, albums = _do_query(lib, query, album, False)
        objs = albums if album else items

        reconfirm = self.check_sanity(mods, dels, objs, album)
        if reconfirm:
            confirm = True

        # Apply changes *temporarily*, preview them, and collect modified
        # objects.
        print_(u'Modifying {0} {1}s.'.format(len(objs),
                                             u'album' if album else u'item'))
        changed = []
        for obj in objs:
            obj_mods = {}
            for key, value in mods.items():
                value = obj.evaluate_template(value)
                obj_mods[key] = model_cls._parse(key, value)

            if print_and_modify(obj, obj_mods, dels) and obj not in changed:
                changed.append(obj)

        # Still something to do?
        if not changed:
            print_(u'No changes to make.')
            return

        # Confirm action.
        if confirm:
            if write and move:
                extra = u', move and write tags'
            elif write:
                extra = u' and write tags'
            elif move:
                extra = u' and move'
            else:
                extra = u''

            changed = ui.input_select_objects(
                u'Really modify%s' % extra, changed,
                lambda o: print_and_modify(o, mods, dels))

        # Apply changes to database and files
        with lib.transaction():
            for obj in changed:
                obj.try_sync(write, move)
Example #59
0
        def func(lib, opts, args):
            # The "write to files" option corresponds to the
            # import_write config value.
            write = config['import']['write'].get(bool)

            for item in lib.items(ui.decargs(args)):
                fetch_item_tempo(lib, logging.INFO, item, write)
                if opts.printbpm and item.bpm:
                    ui.print_('{0} BPM'.format(item.bpm))
Example #60
0
        def func(lib, opts, args):
            # The "write to files" option corresponds to the
            # import_write config value.
            write = config['import']['write'].get(bool)

            for item in lib.items(ui.decargs(args)):
                fetch_item_tempo(lib, logging.INFO, item, write)
                if opts.printbpm and item.bpm:
                    ui.print_('{0} BPM'.format(item.bpm))