diff --git a/beets/library.py b/beets/library.py index e63f9dfed..ff1b46249 100644 --- a/beets/library.py +++ b/beets/library.py @@ -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) + + diff --git a/test/test_db.py b/test/test_db.py index ebcdaf4c1..3e7c9998d 100755 --- a/test/test_db.py +++ b/test/test_db.py @@ -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():