move destination calculation to Library from Item

--HG--
branch : device
This commit is contained in:
Adrian Sampson 2010-04-06 10:07:58 -07:00
parent 83d661152e
commit d3d485195c
2 changed files with 54 additions and 50 deletions

View file

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

View file

@ -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():