Example #1
0
 def test_album_artist_overriden_by_nonempty_track_artist(self):
     my_info = copy.deepcopy(self.info)
     my_info.tracks[0].artist = 'artist1!'
     my_info.tracks[1].artist = 'artist2!'
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, 'artist1!')
     self.assertEqual(self.items[1].artist, 'artist2!')
Example #2
0
 def test_album_artist_overriden_by_nonempty_track_artist(self):
     my_info = copy.deepcopy(self.info)
     my_info.tracks[0].artist = "artist1!"
     my_info.tracks[1].artist = "artist2!"
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, "artist1!")
     self.assertEqual(self.items[1].artist, "artist2!")
Example #3
0
    def write_metadata_changes(self):
        assert self.is_completed, "Processing is not finished for task {}".format(
            self.id)
        apply_metadata(self._album_info_object, self._track_mapping_object)

        for item in self:
            item.write()
Example #4
0
 def _apply(self, info=None, per_disc_numbering=False):
     info = info or self.info
     mapping = {}
     for i, t in zip(self.items, info.tracks):
         mapping[i] = t
     config['per_disc_numbering'] = per_disc_numbering
     autotag.apply_metadata(info, mapping)
Example #5
0
 def test_mb_albumid_and_artistid_applied(self):
     autotag.apply_metadata(self.items, self.info)
     for item in self.items:
         self.assertEqual(item.mb_albumid,
                          '7edb51cb-77d6-4416-a23c-3a8c2994a2c7')
         self.assertEqual(item.mb_artistid,
                          'a6623d39-2d8e-4f70-8242-0a9553b91e50')
Example #6
0
    def albums(self, lib, query, move, pretend, write):
        """Retrieve and apply info from the autotagger for albums matched by
        query and their items.
        """
        # Process matching albums.
        for album in lib.albums(query):
            # Do we have a valid Beatport album?
            items = self.get_album_tracks(album)
            if not items:
                continue

            # Get the Beatport album information.
            albuminfo = self.beatport_plugin.album_for_id(album.mb_albumid)
            if not albuminfo:
                self._log.info(
                    'Release ID {} not found for album {}',
                    album.mb_albumid,
                    album,
                )
                continue

            beatport_trackid_to_trackinfo = {
                track.track_id: track
                for track in albuminfo.tracks
            }
            library_trackid_to_item = {
                int(item.mb_trackid): item
                for item in items
            }
            item_to_trackinfo = {
                item: beatport_trackid_to_trackinfo[track_id]
                for track_id, item in library_trackid_to_item.items()
            }

            self._log.info('applying changes to {}', album)
            with lib.transaction():
                autotag.apply_metadata(albuminfo, item_to_trackinfo)
                changed = False
                # Find any changed item to apply Beatport changes to album.
                any_changed_item = items[0]
                for item in items:
                    item_changed = ui.show_model_changes(item)
                    changed |= item_changed
                    if item_changed:
                        any_changed_item = item
                        apply_item_changes(lib, item, move, pretend, write)

                if pretend or not changed:
                    continue

                # Update album structure to reflect an item in it.
                for key in library.Album.item_keys:
                    album[key] = any_changed_item[key]
                album.store()

                # Move album art (and any inconsistent items).
                if move and lib.directory in util.ancestry(items[0].path):
                    self._log.debug('moving album {}', album)
                    album.move()
Example #7
0
 def test_album_artist_overriden_by_nonempty_track_artist(self):
     my_info = dict(self.info)
     my_info['tracks'] = [dict(t) for t in self.info['tracks']]
     my_info['tracks'][0]['artist'] = 'artist1!'
     my_info['tracks'][1]['artist'] = 'artist2!'
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, 'artist1!')
     self.assertEqual(self.items[1].artist, 'artist2!')
Example #8
0
 def test_album_artist_overriden_by_nonempty_track_artist(self):
     my_info = dict(self.info)
     my_info['tracks'] = [dict(t) for t in self.info['tracks']]
     my_info['tracks'][0]['artist'] = 'artist1!'
     my_info['tracks'][1]['artist'] = 'artist2!'
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, 'artist1!')
     self.assertEqual(self.items[1].artist, 'artist2!')
Example #9
0
def apply_choices(lib, copy, write, art, delete, progress):
    """A coroutine for applying changes to albums during the autotag
    process. The parameters to the generator control the behavior of
    the import. The coroutine accepts (items, info) pairs and yields
    nothing. items the set of Items to import; info is either a
    candidate info dictionary or CHOICE_ASIS.
    """
    lib = _reopen_lib(lib)
    while True:    
        # Get next chunk of work.
        toppath, path, items, info = yield

        # Check for "path finished" message.
        if path is DONE_SENTINEL:
            if progress:
                # Mark path as complete.
                progress_set(toppath, None)
            continue
        
        # Only process the items if info is not None (indicating a
        # skip).
        if info is not None:

            # Change metadata, move, and copy.
            if info is not CHOICE_ASIS:
                autotag.apply_metadata(items, info)
            if copy and delete:
                old_paths = [os.path.realpath(item.path) for item in items]
            for item in items:
                if copy:
                    item.move(lib, True)
                if write and info is not CHOICE_ASIS:
                    item.write()

            # Add items to library. We consolidate this at the end to avoid
            # locking while we do the copying and tag updates.
            albuminfo = lib.add_album(items, infer_aa = (info is CHOICE_ASIS))

            # Get album art if requested.
            if art and info is not CHOICE_ASIS:
                artpath = beets.autotag.art.art_for_album(info)
                if artpath:
                    albuminfo.set_art(artpath)
            
            # Write the database after each album.
            lib.save()

            # Finally, delete old files.
            if copy and delete:
                new_paths = [os.path.realpath(item.path) for item in items]
                for old_path in old_paths:
                    # Only delete files that were actually moved.
                    if old_path not in new_paths:
                        os.remove(library._syspath(old_path))

        # Update progress.
        if progress:
            progress_set(toppath, path)
Example #10
0
 def test_mb_albumartistid_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].mb_albumartistid,
                      '89ad4ac3-39f7-470e-963a-56509c546377')
     self.assertEqual(self.items[1].mb_albumartistid,
                      '89ad4ac3-39f7-470e-963a-56509c546377')
     self.assertEqual(self.items[0].mb_artistid,
                      'a05686fc-9db2-4c23-b99e-77f5db3e5282')
     self.assertEqual(self.items[1].mb_artistid,
                      '80b3cf5e-18fe-4c59-98c7-e5bb87210710')
Example #11
0
File: mbsync.py Project: 241n/beets
def mbsync_albums(lib, query, move, pretend, write):
    """Retrieve and apply info from the autotagger for albums matched by
    query and their items.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid'.format(a.id))
            continue

        items = list(a.items())

        # Get the MusicBrainz album information.
        album_info = hooks.album_for_mbid(a.mb_albumid)
        if not album_info:
            log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
            continue

        # Construct a track mapping according to MBIDs. This should work
        # for albums that have missing or extra tracks.
        mapping = {}
        for item in items:
            for track_info in album_info.tracks:
                if item.mb_trackid == track_info.track_id:
                    mapping[item] = track_info
                    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):
                    log.debug(u'moving album {0}'.format(a.id))
                    a.move()
Example #12
0
def mbsync_albums(lib, query, move, pretend, write):
    """Retrieve and apply info from the autotagger for albums matched by
    query and their items.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid'.format(a.id))
            continue

        items = list(a.items())

        # Get the MusicBrainz album information.
        album_info = hooks.album_for_mbid(a.mb_albumid)
        if not album_info:
            log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
            continue

        # Construct a track mapping according to MBIDs. This should work
        # for albums that have missing or extra tracks.
        mapping = {}
        for item in items:
            for track_info in album_info.tracks:
                if item.mb_trackid == track_info.track_id:
                    mapping[item] = track_info
                    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):
                    log.debug(u'moving album {0}'.format(a.id))
                    a.move()
Example #13
0
def mbsync_albums(lib, query, move, pretend, write):
    """Synchronize matching albums.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid'.format(a.id))
            continue

        items = list(a.items())
        for item in items:
            item.old_data = dict(item.record)

        # Get the MusicBrainz album information.
        matches = hooks._album_for_id(a.mb_albumid)
        if not matches:
            log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
            continue
        album_info = matches[0]

        # Construct a track mapping according to MBIDs. This should work
        # for albums that have missing or extra tracks.
        mapping = {}
        for item in items:
            for track_info in album_info.tracks:
                if item.mb_trackid == track_info.track_id:
                    mapping[item] = track_info
                    break

        # Apply.
        with lib.transaction():
            autotag.apply_metadata(album_info, mapping)
            changed = False
            for item in items:
                changed = _print_and_apply_changes(lib, item, move, pretend,
                    write) or changed
            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_KEYS_ITEM:
                    setattr(a, key, getattr(items[0], key))

                # Move album art (and any inconsistent items).
                if move and lib.directory in util.ancestry(items[0].path):
                    log.debug(u'moving album {0}'.format(a.id))
                    a.move()
Example #14
0
def mbsync_albums(lib, query, move, pretend, write):
    """Synchronize matching albums.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid'.format(a.id))
            continue

        items = list(a.items())
        for item in items:
            item.old_data = dict(item.record)

        # Get the MusicBrainz album information.
        matches = hooks._album_for_id(a.mb_albumid)
        if not matches:
            log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
            continue
        album_info = matches[0]

        # Construct a track mapping according to MBIDs. This should work
        # for albums that have missing or extra tracks.
        mapping = {}
        for item in items:
            for track_info in album_info.tracks:
                if item.mb_trackid == track_info.track_id:
                    mapping[item] = track_info
                    break

        # Apply.
        with lib.transaction():
            autotag.apply_metadata(album_info, mapping)
            changed = False
            for item in items:
                changed = _print_and_apply_changes(lib, item, move, pretend,
                                                   write) or changed
            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_KEYS_ITEM:
                    setattr(a, key, getattr(items[0], key))

                # Move album art (and any inconsistent items).
                if move and lib.directory in util.ancestry(items[0].path):
                    log.debug(u'moving album {0}'.format(a.id))
                    a.move()
Example #15
0
    def show_changes(self, lib, task, match=None):
        if match is None:
            match = task.match

        changes = False
        if task.is_album:
            newmapping = {new_item(item): track_info for item, track_info in match.mapping.items()}
            autotag.apply_metadata(match.info, newmapping)

            # olditems[0].get_album() isn't working, create our own to compare
            olditems = list(match.mapping.keys())
            oldvalues = dict((key, olditems[0][key]) for key in self.album_fields)
            oldalbum = library.Album(lib, **oldvalues)

            newitems = list(newmapping.keys())
            values = dict((key, newitems[0][key]) for key in self.album_fields)
            album = library.Album(lib, **values)
            compare_fields = self.get_fields(self.album_fields, oldvalues)

            album_changes = show_model_changes(album, oldalbum, compare_fields)
            if album_changes:
                changes = True

            new_by_info = {track_info: item for item, track_info in newmapping.items()}
            for item, track_info in match.mapping.items():
                newitem = new_by_info[track_info]
                compare_fields = self.get_fields(self.nonalbum_fields, item)
                item_changes = show_model_changes(newitem, item, compare_fields)
                if item_changes:
                    changes = True
        else:
            fakeitem = new_item(task.item)
            autotag.apply_item_metadata(fakeitem, match.info)
            compare_fields = self.get_fields(self.all_fields, task.item)
            changes = show_model_changes(fakeitem, task.item, compare_fields)

        return changes
Example #16
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process. The parameters to the generator control the behavior of
    the import. The coroutine accepts ImportTask objects and yields
    nothing.
    """
    lib = _reopen_lib(config.lib)
    while True:    
        task = yield
        # Don't do anything if we're skipping the album or we're done.
        if task.sentinel or task.choice_flag == action.SKIP:
            if config.resume is not False:
                task.save_progress()
            continue

        # Change metadata, move, and copy.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)
        items = task.items if task.is_album else [task.item]
        if config.copy and config.delete:
            old_paths = [os.path.realpath(syspath(item.path)) for item in items]
        for item in items:
            if config.copy:
                item.move(lib, True, task.should_create_album())
            if config.write and task.should_write_tags():
                item.write()

        # Add items to library. We consolidate this at the end to avoid
        # locking while we do the copying and tag updates.
        if task.should_create_album():
            # Add an album.
            albuminfo = lib.add_album(task.items,
                                      infer_aa = task.should_infer_aa())
        else:
            # Add tracks.
            for item in items:
                lib.add(item)
        lib.save()

        # Get album art if requested.
        if config.art and task.should_fetch_art():
            artpath = beets.autotag.art.art_for_album(task.info)
            if artpath:
                albuminfo.set_art(artpath)
        lib.save()

        # Announce that we've added an album.
        if task.should_create_album():
            plugins.send('album_imported', lib=lib, album=albuminfo)
        else:
            plugins.send('item_imported', lib=lib, item=task.item)

        # Finally, delete old files.
        if config.copy and config.delete:
            new_paths = [os.path.realpath(item.path) for item in items]
            for old_path in old_paths:
                # Only delete files that were actually moved.
                if old_path not in new_paths:
                    os.remove(syspath(old_path))

        # Update progress.
        if config.resume is not False:
            task.save_progress()
Example #17
0
 def test_disc_total_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].disctotal, 2)
     self.assertEqual(self.items[1].disctotal, 2)
Example #18
0
 def test_albumtype_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].albumtype, "album")
     self.assertEqual(self.items[1].albumtype, "album")
Example #19
0
 def test_titles_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].title, "oneNew")
     self.assertEqual(self.items[1].title, "twoNew")
Example #20
0
 def test_track_index_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].track, 1)
     self.assertEqual(self.items[1].track, 2)
Example #21
0
 def apply_metadata(self):
     """Copy metadata from match info to the items.
     """
     # TODO call should be more descriptive like
     # apply_metadata(self.match, self.items)
     autotag.apply_metadata(self.match.info, self.match.mapping)
Example #22
0
 def test_mb_trackid_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].mb_trackid,
                     'dfa939ec-118c-4d0f-84a0-60f3d1e6522c')
     self.assertEqual(self.items[1].mb_trackid,
                      '40130ed1-a27c-42fd-a328-1ebefb6caef4')
Example #23
0
 def test_album_and_track_artists_separate(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].artist, 'artistOneNew')
     self.assertEqual(self.items[1].artist, 'artistTwoNew')
     self.assertEqual(self.items[0].albumartist, 'variousNew')
     self.assertEqual(self.items[1].albumartist, 'variousNew')
Example #24
0
 def test_track_index_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].track, 1)
     self.assertEqual(self.items[1].track, 2)
Example #25
0
 def test_disc_index_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].disc, 1)
     self.assertEqual(self.items[1].disc, 2)
Example #26
0
 def test_artist_sort_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].albumartist_sort, 'albumArtistSort')
     self.assertEqual(self.items[0].artist_sort, 'trackArtistSort')
     self.assertEqual(self.items[1].albumartist_sort, 'albumArtistSort')
     self.assertEqual(self.items[1].artist_sort, 'albumArtistSort')
Example #27
0
 def test_album_artist_overrides_empty_track_artist(self):
     my_info = copy.deepcopy(self.info)
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, 'artistNew')
     self.assertEqual(self.items[0].artist, 'artistNew')
Example #28
0
 def test_albumtype_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].albumtype, 'album')
     self.assertEqual(self.items[1].albumtype, 'album')
Example #29
0
 def test_disc_total_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].disctotal, 2)
     self.assertEqual(self.items[1].disctotal, 2)
Example #30
0
 def test_va_flag_cleared_does_not_set_comp(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertFalse(self.items[0].comp)
     self.assertFalse(self.items[1].comp)
Example #31
0
 def test_va_flag_cleared_does_not_set_comp(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertFalse(self.items[0].comp)
     self.assertFalse(self.items[1].comp)
Example #32
0
 def apply_metadata(self):
     """Copy metadata from match info to the items.
     """
     autotag.apply_metadata(self.match.info, self.match.mapping)
Example #33
0
 def test_va_flag_sets_comp(self):
     va_info = copy.deepcopy(self.info)
     va_info.va = True
     autotag.apply_metadata(self.items, va_info)
     self.assertTrue(self.items[0].comp)
     self.assertTrue(self.items[1].comp)
Example #34
0
 def test_va_flag_sets_comp(self):
     va_info = dict(self.info) # make a copy
     va_info['va'] = True
     autotag.apply_metadata(self.items, va_info)
     self.assertTrue(self.items[0].comp)
     self.assertTrue(self.items[1].comp)
Example #35
0
 def test_album_and_artist_applied_to_all(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].album, 'albumNew')
     self.assertEqual(self.items[1].album, 'albumNew')
     self.assertEqual(self.items[0].artist, 'artistNew')
     self.assertEqual(self.items[1].artist, 'artistNew')
Example #36
0
 def apply_metadata(self):
     """Copy metadata from match info to the items.
     """
     autotag.apply_metadata(self.match.info, self.match.mapping)
Example #37
0
 def test_titles_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].title, 'oneNew')
     self.assertEqual(self.items[1].title, 'twoNew')
Example #38
0
 def test_album_and_artist_applied_to_all(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].album, "albumNew")
     self.assertEqual(self.items[1].album, "albumNew")
     self.assertEqual(self.items[0].artist, "artistNew")
     self.assertEqual(self.items[1].artist, "artistNew")
Example #39
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process.
    """
    lib = _reopen_lib(config.lib)
    task = None
    while True:    
        task = yield task
        if task.should_skip():
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing. Old
        # album structures are automatically cleaned up when the
        # last item is removed.
        replaced_items = defaultdict(list)
        for item in items:
            dup_items = lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(replaced_items),
                                               len(items)))

        # Move/copy files.
        task.old_paths = [item.path for item in items]
        for item in items:
            if config.copy:
                # If we're replacing an item, then move rather than
                # copying.
                do_copy = not bool(replaced_items[item])
                lib.move(item, do_copy, task.is_album)
            if config.write and task.should_write_tags():
                item.write()

        # Add items to library. We consolidate this at the end to avoid
        # locking while we do the copying and tag updates.
        try:
            # Remove old items.
            for replaced in replaced_items.itervalues():
                for item in replaced:
                    lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    lib.add(item)
        finally:
            lib.save()
Example #40
0
 def test_disc_index_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].disc, 1)
     self.assertEqual(self.items[1].disc, 2)
Example #41
0
    def albums(self, lib, query, move, pretend, write):
        """Retrieve and apply info from the autotagger for albums matched by
        query and their items.
        """
        # Process matching albums.
        for a in lib.albums(query):
            album_formatted = format(a)
            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 release track and recording MBIDs to their information.
            # Recordings can appear multiple times on a release, so each MBID
            # maps to a list of TrackInfo objects.
            releasetrack_index = dict()
            track_index = defaultdict(list)
            for track_info in album_info.tracks:
                releasetrack_index[track_info.release_track_id] = track_info
                track_index[track_info.track_id].append(track_info)

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

            # Apply.
            self._log.debug(u'applying changes to {}', album_formatted)
            with lib.transaction():
                autotag.apply_metadata(album_info, mapping)
                changed = False
                # Find any changed item to apply MusicBrainz changes to album.
                any_changed_item = items[0]
                for item in items:
                    item_changed = ui.show_model_changes(item)
                    changed |= item_changed
                    if item_changed:
                        any_changed_item = item
                        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] = any_changed_item[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()
Example #42
0
 def test_mb_trackid_applied(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].mb_trackid, "dfa939ec-118c-4d0f-84a0-60f3d1e6522c")
     self.assertEqual(self.items[1].mb_trackid, "40130ed1-a27c-42fd-a328-1ebefb6caef4")
Example #43
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process.
    """
    lib = _reopen_lib(config.lib)
    task = None
    while True:    
        task = yield task
        if task.should_skip():
            continue

        items = task.all_items()
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)
            plugins.send('import_task_apply', config=config, task=task)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        replaced_items = defaultdict(list)
        for item in items:
            dup_items = lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(replaced_items),
                                               len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.soft_remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    lib.directory)

        # Add items -- before path changes -- to the library. We add the
        # items now (rather than at the end) so that album structures
        # are in place before calls to destination().
        try:
            # Remove old items.
            for replaced in replaced_items.itervalues():
                for item in replaced:
                    lib.remove(item)
            for item in duplicate_items:
                lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    lib.add(item)
        finally:
            lib.save()

        # Move/copy files.
        task.old_paths = [item.path for item in items]  # For deletion.
        for item in items:
            if config.copy or config.move:
                if config.move:
                    # Just move the file.
                    lib.move(item, False)
                else:
                    # If it's a reimport, move the file. Otherwise, copy
                    # and keep track of the old path.
                    old_path = item.path
                    do_copy = not bool(replaced_items[item])
                    lib.move(item, do_copy)
                    if not do_copy:
                        # If we moved the item, remove the now-nonexistent
                        # file from old_paths.
                        task.old_paths.remove(old_path)

            if config.write and task.should_write_tags():
                item.write()

        # Save new paths.
        try:
            for item in items:
                lib.store(item)
        finally:
            lib.save()
Example #44
0
 def test_album_artist_overrides_empty_track_artist(self):
     my_info = copy.deepcopy(self.info)
     autotag.apply_metadata(self.items, my_info)
     self.assertEqual(self.items[0].artist, "artistNew")
     self.assertEqual(self.items[0].artist, "artistNew")
Example #45
0
    def write_metadata_changes(self):
        assert self.is_completed, "Processing is not finished for task {}".format(self.id)
        apply_metadata(self._album_info_object, self._track_mapping_object)

        for item in self:
            item.write()
Example #46
0
 def test_album_and_track_artists_separate(self):
     autotag.apply_metadata(self.items, self.info)
     self.assertEqual(self.items[0].artist, "artistOneNew")
     self.assertEqual(self.items[1].artist, "artistTwoNew")
     self.assertEqual(self.items[0].albumartist, "variousNew")
     self.assertEqual(self.items[1].albumartist, "variousNew")
Example #47
0
            return
        if str(rec) == 'recommendation.none':
            logger.warn('No accurate album match found for %s, %s -  not writing metadata', release['ArtistName'], release['AlbumTitle'])
            return
        
        if candidates:
            dist, info, mapping, extra_items, extra_tracks = candidates[0]
        else:
            logger.warn('No accurate album match found for %s, %s -  not writing metadata', release['ArtistName'], release['AlbumTitle'])
            return
        
        logger.info('Beets recommendation for tagging items: %s' % rec)

        # TODO: Handle extra_items & extra_tracks
        
        autotag.apply_metadata(info, mapping)
        
        for item in items:
            try:
                item.write()
                logger.info("Successfully applied metadata to: %s", item.path.decode(headphones.SYS_ENCODING, 'replace'))
            except Exception, e:
                logger.warn("Error writing metadata to '%s': %s", item.path.decode(headphones.SYS_ENCODING, 'replace'), str(e))
        
def embedLyrics(downloaded_track_list):
    logger.info('Adding lyrics')
    
    # TODO: If adding lyrics for flac & lossy, only fetch the lyrics once
    # and apply it to both files
    for downloaded_track in downloaded_track_list:
        
Example #48
0
 def test_va_flag_sets_comp(self):
     va_info = copy.deepcopy(self.info)
     va_info.va = True
     autotag.apply_metadata(self.items, va_info)
     self.assertTrue(self.items[0].comp)
     self.assertTrue(self.items[1].comp)
Example #49
0
 def apply_metadata(self):
     """Copy metadata from match info to the items.
     """
     # TODO call should be more descriptive like
     # apply_metadata(self.match, self.items)
     autotag.apply_metadata(self.match.info, self.match.mapping)
Example #50
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process.
    """
    lib = _reopen_lib(config.lib)
    task = None
    while True:    
        task = yield task
        if task.should_skip():
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing. Old
        # album structures are automatically cleaned up when the
        # last item is removed.
        replaced_items = defaultdict(list)
        for item in items:
            dup_items = lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(replaced_items),
                                               len(items)))

        # Move/copy files.
        task.old_paths = [item.path for item in items]
        for item in items:
            if config.copy:
                # If we're replacing an item, then move rather than
                # copying.
                old_path = item.path
                do_copy = not bool(replaced_items[item])
                lib.move(item, do_copy, task.is_album)
                if not do_copy:
                    # If we moved the item, remove the now-nonexistent
                    # file from old_paths.
                    task.old_paths.remove(old_path)
            if config.write and task.should_write_tags():
                item.write()

        # Add items to library. We consolidate this at the end to avoid
        # locking while we do the copying and tag updates.
        try:
            # Remove old items.
            for replaced in replaced_items.itervalues():
                for item in replaced:
                    lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    lib.add(item)
        finally:
            lib.save()
Example #51
0
def mbsync_albums(lib, query, move, pretend, write):
    """Retrieve and apply info from the autotagger for albums matched by
    query and their items.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid'.format(a.id))
            continue

        items = list(a.items())

        # Get the MusicBrainz album information.
        album_info = hooks.album_for_mbid(a.mb_albumid)
        if not album_info:
            log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
            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):
                    log.debug(u'moving album {0}'.format(a.id))
                    a.move()
Example #52
0
def mbsync_albums(lib, query, move, pretend, write):
    """Retrieve and apply info from the autotagger for albums matched by
    query and their items.
    """
    # Process matching albums.
    for a in lib.albums(query):
        if not a.mb_albumid:
            log.info(u'Skipping album {0}: has no mb_albumid', a.id)
            continue

        items = list(a.items())

        # Get the MusicBrainz album information.
        album_info = hooks.album_for_mbid(a.mb_albumid)
        if not album_info:
            log.info(u'Release ID not found: {0}', a.mb_albumid)
            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):
                    log.debug(u'moving album {0}', a.id)
                    a.move()
Example #53
0
def apply_choices(session):
    """A coroutine for applying changes to albums and singletons during
    the autotag process.
    """
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        items = task.imported_items()
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(
                    task.match.info, task.match.mapping
                )
            else:
                autotag.apply_item_metadata(task.item, task.match.info)
            plugins.send('import_task_apply', session=session, task=task)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        task.replaced_items = defaultdict(list)
        for item in items:
            dup_items = session.lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                task.replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(task.replaced_items),
                                               len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(session.lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(session.lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if session.lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    session.lib.directory)

        # Add items -- before path changes -- to the library. We add the
        # items now (rather than at the end) so that album structures
        # are in place before calls to destination().
        with session.lib.transaction():
            # Remove old items.
            for replaced in task.replaced_items.itervalues():
                for item in replaced:
                    session.lib.remove(item)
            for item in duplicate_items:
                session.lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = session.lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    session.lib.add(item)
Example #54
0
def apply_choices(session):
    """A coroutine for applying changes to albums and singletons during
    the autotag process.
    """
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        items = task.imported_items()
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.match.info, task.match.mapping)
            else:
                autotag.apply_item_metadata(task.item, task.match.info)
            plugins.send('import_task_apply', session=session, task=task)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        task.replaced_items = defaultdict(list)
        for item in items:
            dup_items = session.lib.items(library.MatchQuery(
                'path', item.path))
            for dup_item in dup_items:
                task.replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' %
                  (len(task.replaced_items), len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(session.lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(session.lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if session.lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    session.lib.directory)

        # Add items -- before path changes -- to the library. We add the
        # items now (rather than at the end) so that album structures
        # are in place before calls to destination().
        with session.lib.transaction():
            # Remove old items.
            for replaced in task.replaced_items.itervalues():
                for item in replaced:
                    session.lib.remove(item)
            for item in duplicate_items:
                session.lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = session.lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    session.lib.add(item)
Example #55
0
 def test_per_disc_numbering(self):
     autotag.apply_metadata(self.items, self.info,
                            per_disc_numbering=True)
     self.assertEqual(self.items[0].track, 1)
     self.assertEqual(self.items[1].track, 1)