Skip to content

Commit

Permalink
Add duplicate_keys feature for singletons
Browse files Browse the repository at this point in the history
  • Loading branch information
jcassette committed Jan 22, 2022
1 parent f50d250 commit 7633465
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 14 deletions.
4 changes: 3 additions & 1 deletion beets/config_default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import:
group_albums: no
pretend: false
search_ids: []
duplicate_keys: albumartist album
duplicate_keys:
album: albumartist album
single: artist title
duplicate_action: ask
bell: no
set_fields: {}
Expand Down
27 changes: 16 additions & 11 deletions beets/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ def find_duplicates(self, lib):

duplicates = []
task_paths = {i.path for i in self.items if i}
keys = config['import']['duplicate_keys'].as_str_seq()
keys = config['import']['duplicate_keys']['album'].as_str_seq()
info['albumartist'] = info['artist']
# Create an Album object so that flexible attributes can be used.
tmp_album = library.Album(lib, **info)
Expand Down Expand Up @@ -893,12 +893,17 @@ def __init__(self, toppath, item):
self.is_album = False
self.paths = [item.path]

def chosen_ident(self):
assert self.choice_flag in (action.ASIS, action.APPLY, action.RETAG)
def chosen_info(self):
"""Return a dictionary of metadata about the current choice.
May only be called when the choice flag is ASIS or RETAG
(in which case the data comes from the files' current metadata)
or APPLY (in which case the data comes from the choice).
"""
assert self.choice_flag in (action.ASIS, action.RETAG, action.APPLY)
if self.choice_flag in (action.ASIS, action.RETAG):
return (self.item.artist, self.item.title)
return dict(self.item)
elif self.choice_flag is action.APPLY:
return (self.match.info.artist, self.match.info.title)
return self.match.info.copy()

def imported_items(self):
return [self.item]
Expand All @@ -919,14 +924,14 @@ def find_duplicates(self, lib):
"""Return a list of items from `lib` that have the same artist
and title as the task.
"""
artist, title = self.chosen_ident()
info = self.chosen_info()

found_items = []
query = dbcore.AndQuery((
dbcore.MatchQuery('artist', artist),
dbcore.MatchQuery('title', title),
))
for other_item in lib.items(query):
keys = config['import']['duplicate_keys']['single'].as_str_seq()
# Create an Item object so that flexible attributes can be used.
tmp_item = library.Item(lib, **info)

for other_item in tmp_item.duplicates(*keys):
# Existing items not considered duplicates.
if other_item.path != self.item.path:
found_items.append(other_item)
Expand Down
14 changes: 14 additions & 0 deletions beets/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,20 @@ def from_path(cls, path):
i.mtime = i.current_mtime() # Initial mtime.
return i

@classmethod
def construct_match_queries(cls, **info):
subqueries = []
for (key, value) in info.items():
# Use slow queries for flexible attributes.
fast = key in cls._fields
subqueries.append(dbcore.MatchQuery(key, value, fast))
return subqueries

def duplicates(self, *keys):
info = {key: self.get(key) for key in keys}
subqueries = self.construct_match_queries(**info)
return self._db.items(dbcore.AndQuery(subqueries))

def __setitem__(self, key, value):
"""Set the item's value for a standard field or a flexattr."""
# Encode unicode paths and read buffers.
Expand Down
17 changes: 15 additions & 2 deletions test/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,7 @@ def setUp(self):
# Create import session
self.importer = self.create_importer()
config['import']['autotag'] = True
config['import']['duplicate_keys'] = 'albumartist album'
config['import']['duplicate_keys']['album'] = 'albumartist album'

def tearDown(self):
self.teardown_beets()
Expand Down Expand Up @@ -1313,7 +1313,7 @@ def test_twice_in_import_dir(self):
self.skipTest('write me')

def test_keep_when_extra_key_is_different(self):
config['import']['duplicate_keys'] = 'albumartist album flex'
config['import']['duplicate_keys']['album'] = 'albumartist album flex'

item = self.lib.items().get()
import_file = MediaFile(os.path.join(
Expand Down Expand Up @@ -1359,6 +1359,7 @@ def setUp(self):
self.importer = self.create_importer()
config['import']['autotag'] = True
config['import']['singletons'] = True
config['import']['duplicate_keys']['single'] = 'artist title'

def tearDown(self):
self.teardown_beets()
Expand Down Expand Up @@ -1395,6 +1396,18 @@ def test_skip_duplicate(self):
item = self.lib.items().get()
self.assertEqual(item.mb_trackid, 'old trackid')

def test_keep_when_extra_key_is_different(self):
config['import']['duplicate_keys']['single'] = 'artist title flex'
item = self.lib.items().get()
item.flex = 'different'
item.store()
self.assertEqual(len(self.lib.items()), 1)

self.importer.default_resolution = self.importer.Resolution.SKIP
self.importer.run()

self.assertEqual(len(self.lib.items()), 2)

def test_twice_in_import_dir(self):
self.skipTest('write me')

Expand Down

0 comments on commit 7633465

Please sign in to comment.