mirror of
https://github.com/beetbox/beets.git
synced 2026-02-01 04:55:38 +01:00
move destination calculation to Library from Item
--HG-- branch : device
This commit is contained in:
parent
83d661152e
commit
d3d485195c
2 changed files with 54 additions and 50 deletions
|
|
@ -254,46 +254,6 @@ class Item(object):
|
|||
|
||||
#### dealing with files themselves ####
|
||||
|
||||
def destination(self):
|
||||
"""Returns the path within the library directory designated for this
|
||||
item (i.e., where the file ought to be).
|
||||
"""
|
||||
libpath = self.library.directory
|
||||
subpath_tmpl = Template(self.library.path_format)
|
||||
|
||||
# build the mapping for substitution in the path template, beginning
|
||||
# with the values from the database
|
||||
mapping = {}
|
||||
for key in metadata_keys:
|
||||
value = getattr(self, key)
|
||||
# sanitize the value for inclusion in a path:
|
||||
# replace / and leading . with _
|
||||
if isinstance(value, basestring):
|
||||
value.replace(os.sep, '_')
|
||||
value = re.sub(r'[\\/:]|^\.', '_', value)
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# pad with zeros
|
||||
value = '%02i' % value
|
||||
else:
|
||||
value = str(value)
|
||||
mapping[key] = value
|
||||
|
||||
# Perform substitution.
|
||||
subpath = subpath_tmpl.substitute(mapping)
|
||||
|
||||
# Truncate path components.
|
||||
comps = _components(subpath)
|
||||
for i, comp in enumerate(comps):
|
||||
if len(comp) > MAX_FILENAME_LENGTH:
|
||||
comps[i] = comp[:MAX_FILENAME_LENGTH]
|
||||
subpath = os.path.join(*comps)
|
||||
|
||||
# Preserve extension.
|
||||
_, extension = os.path.splitext(self.path)
|
||||
subpath += extension
|
||||
|
||||
return _normpath(os.path.join(libpath, subpath))
|
||||
|
||||
def move(self, copy=False):
|
||||
"""Move the item to its designated location within the library
|
||||
directory (provided by destination()). Subdirectories are created as
|
||||
|
|
@ -308,7 +268,7 @@ class Item(object):
|
|||
Note that one should almost certainly call store() and library.save()
|
||||
after this method in order to keep on-disk data consistent.
|
||||
"""
|
||||
dest = self.destination()
|
||||
dest = self.library.destination(self)
|
||||
|
||||
# Create necessary ancestry for the move. Like os.renames but only
|
||||
# halfway.
|
||||
|
|
@ -689,7 +649,46 @@ class Library(BaseLibrary):
|
|||
|
||||
self.conn.executescript(setup_sql)
|
||||
self.conn.commit()
|
||||
|
||||
|
||||
def destination(self, item):
|
||||
"""Returns the path in the library directory designated for item
|
||||
item (i.e., where the file ought to be).
|
||||
"""
|
||||
libpath = self.directory
|
||||
subpath_tmpl = Template(self.path_format)
|
||||
|
||||
# build the mapping for substitution in the path template, beginning
|
||||
# with the values from the database
|
||||
mapping = {}
|
||||
for key in metadata_keys:
|
||||
value = getattr(item, key)
|
||||
# sanitize the value for inclusion in a path:
|
||||
# replace / and leading . with _
|
||||
if isinstance(value, basestring):
|
||||
value.replace(os.sep, '_')
|
||||
value = re.sub(r'[\\/:]|^\.', '_', value)
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# pad with zeros
|
||||
value = '%02i' % value
|
||||
else:
|
||||
value = str(value)
|
||||
mapping[key] = value
|
||||
|
||||
# Perform substitution.
|
||||
subpath = subpath_tmpl.substitute(mapping)
|
||||
|
||||
# Truncate path components.
|
||||
comps = _components(subpath)
|
||||
for i, comp in enumerate(comps):
|
||||
if len(comp) > MAX_FILENAME_LENGTH:
|
||||
comps[i] = comp[:MAX_FILENAME_LENGTH]
|
||||
subpath = os.path.join(*comps)
|
||||
|
||||
# Preserve extension.
|
||||
_, extension = os.path.splitext(item.path)
|
||||
subpath += extension
|
||||
|
||||
return _normpath(os.path.join(libpath, subpath))
|
||||
|
||||
#### main interface ####
|
||||
|
||||
|
|
@ -803,3 +802,5 @@ class Library(BaseLibrary):
|
|||
c = self.conn.execute(sql, subvals)
|
||||
return ResultIterator(c, self)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -154,12 +154,12 @@ class DestinationTest(unittest.TestCase):
|
|||
def test_directory_works_with_trailing_slash(self):
|
||||
self.lib.directory = 'one/'
|
||||
self.lib.path_format = 'two'
|
||||
self.assertEqual(self.i.destination(), np('one/two'))
|
||||
self.assertEqual(self.lib.destination(self.i), np('one/two'))
|
||||
|
||||
def test_directory_works_without_trailing_slash(self):
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_format = 'two'
|
||||
self.assertEqual(self.i.destination(), np('one/two'))
|
||||
self.assertEqual(self.lib.destination(self.i), np('one/two'))
|
||||
|
||||
def test_destination_substitues_metadata_values(self):
|
||||
self.lib.directory = 'base'
|
||||
|
|
@ -167,13 +167,15 @@ class DestinationTest(unittest.TestCase):
|
|||
self.i.title = 'three'
|
||||
self.i.artist = 'two'
|
||||
self.i.album = 'one'
|
||||
self.assertEqual(self.i.destination(), np('base/one/two three'))
|
||||
self.assertEqual(self.lib.destination(self.i),
|
||||
np('base/one/two three'))
|
||||
|
||||
def test_destination_preserves_extension(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_format = '$title'
|
||||
self.i.path = 'hey.audioFormat'
|
||||
self.assertEqual(self.i.destination(),np('base/the title.audioFormat'))
|
||||
self.assertEqual(self.lib.destination(self.i),
|
||||
np('base/the title.audioFormat'))
|
||||
|
||||
def test_destination_pads_some_indices(self):
|
||||
self.lib.directory = 'base'
|
||||
|
|
@ -185,11 +187,12 @@ class DestinationTest(unittest.TestCase):
|
|||
self.i.disctotal = 4
|
||||
self.i.bpm = 5
|
||||
self.i.year = 6
|
||||
self.assertEqual(self.i.destination(), np('base/01 02 03 04 5 6'))
|
||||
self.assertEqual(self.lib.destination(self.i),
|
||||
np('base/01 02 03 04 5 6'))
|
||||
|
||||
def test_destination_escapes_slashes(self):
|
||||
self.i.album = 'one/two'
|
||||
dest = self.i.destination()
|
||||
dest = self.lib.destination(self.i)
|
||||
self.assertTrue('one' in dest)
|
||||
self.assertTrue('two' in dest)
|
||||
self.assertFalse('one/two' in dest)
|
||||
|
|
@ -197,13 +200,13 @@ class DestinationTest(unittest.TestCase):
|
|||
def test_destination_long_names_truncated(self):
|
||||
self.i.title = 'X'*300
|
||||
self.i.artist = 'Y'*300
|
||||
for c in self.i.destination().split(os.path.sep):
|
||||
for c in self.lib.destination(self.i).split(os.path.sep):
|
||||
self.assertTrue(len(c) <= 255)
|
||||
|
||||
def test_destination_long_names_keep_extension(self):
|
||||
self.i.title = 'X'*300
|
||||
self.i.path = 'something.extn'
|
||||
dest = self.i.destination()
|
||||
dest = self.lib.destination(self.i)
|
||||
self.assertEqual(dest[-5:], '.extn')
|
||||
|
||||
def suite():
|
||||
|
|
|
|||
Loading…
Reference in a new issue