Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions beets/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,10 @@ class BaseAlbum(object):
album-level metadata or use distinct backing stores.
"""
def __init__(self, library, record):
self._library = library
self._record = record
# Need to use object.__setattr__ here, since we're overriding
# it for this class
object.__setattr__(self, '_library', library)
object.__setattr__(self, '_record', record)

def __getattr__(self, key):
"""Get the value for an album attribute."""
Expand Down Expand Up @@ -1087,10 +1089,16 @@ def add_album(self, items):
"""
# Set the metadata from the first item.
#fixme: check for consensus?
item_values = dict(
(key, getattr(items[0], key)) for key in ALBUM_KEYS_ITEM)
if not item_values['albumartist']:
item_values['albumartist'] = getattr(items[0], 'artist')


sql = 'INSERT INTO albums (%s) VALUES (%s)' % \
(', '.join(ALBUM_KEYS_ITEM),
', '.join(['?'] * len(ALBUM_KEYS_ITEM)))
subvals = [getattr(items[0], key) for key in ALBUM_KEYS_ITEM]
subvals = [item_values[key] for key in ALBUM_KEYS_ITEM]
c = self.conn.execute(sql, subvals)
album_id = c.lastrowid

Expand Down
17 changes: 12 additions & 5 deletions beets/ui/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ def apply_choices(lib, copy, write, art, delete):
if info is not CHOICE_ASIS:
autotag.apply_metadata(items, info)
if copy and delete:
old_paths = [item.path for item in items]
old_paths = [os.path.realpath(item.path) for item in items]
for item in items:
if copy:
item.move(lib, True)
Expand All @@ -488,8 +488,11 @@ def apply_choices(lib, copy, write, art, delete):

# 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:
os.remove(library._syspath(old_path))
if old_path not in new_paths:
os.remove(library._syspath(old_path))
os.remove(library._syspath(old_path))

# Update progress.
progress_set(toppath, path)
Expand All @@ -506,7 +509,7 @@ def simple_import(lib, paths, copy, delete):

if copy:
if delete:
old_paths = [item.path for item in items]
old_paths = [os.path.realpath(item.path) for item in items]
for item in items:
item.move(lib, True)

Expand All @@ -515,10 +518,14 @@ def simple_import(lib, paths, copy, delete):
progress_set(toppath, path)

if copy and delete:
new_paths = [os.path.realpath(item.path) for item in items]
for old_path in old_paths:
os.remove(library._syspath(old_path))
# Only delete the path if it isn't a file we just created.
if old_path not in new_paths:
os.remove(library._syspath(old_path))


log.info('added album: %s - %s' % (album.artist, album.album))
log.info('added album: %s - %s' % (album.albumartist, album.album))

# The import command.

Expand Down
45 changes: 45 additions & 0 deletions test/test_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file is part of beets.
# Copyright 2010, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

"""Basic tests for the objects that are used to represent the beets
library and the items in it.
"""

import unittest
import sys
sys.path.append('..')
from beets.library import BaseLibrary, BaseAlbum

class AlbumTest(unittest.TestCase):

def test_field_access(self):
album = BaseAlbum(None, {'artist':'foo', 'albumartist':'bar'})
self.assertEqual(album.artist, 'foo')
self.assertEqual(album.albumartist, 'bar')

def test_field_access_unset_values(self):
"""
This is how things work currently. Trying to access unset album
metadata raises an AttributeError.
"""
album = BaseAlbum(None, {})
self.assertRaises(AttributeError, getattr, album, 'albumartist')
self.assertRaises(AttributeError, getattr, album, 'artist')

def suite():
return unittest.TestLoader().loadTestsFromName(__name__)

if __name__ == '__main__':
unittest.main(defaultTest='suite')

79 changes: 79 additions & 0 deletions test/test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import unittest
import sys
import os
import shutil
import textwrap
from StringIO import StringIO
import _common
Expand All @@ -26,8 +27,86 @@
from beets import ui
from beets.ui import commands
from beets import autotag
from beets import mediafile
import test_db

class ImportTest(unittest.TestCase):
def setUp(self):
self.io = _common.DummyIO()
self.io.install()

self.lib = library.Library(':memory:')
self.libdir = os.path.join('rsrc', 'testlibdir')
self.lib.directory = self.libdir
self.lib.path_formats = {'default': os.path.join('$artist', '$album', '$title')}

self.srcdir = os.path.join('rsrc', 'testsrcdir')

def tearDown(self):
self.io.restore()
if os.path.exists(self.libdir):
shutil.rmtree(self.libdir)
if os.path.exists(self.srcdir):
shutil.rmtree(self.srcdir)

def create_test_file(self, filepath, metadata):
"""
Creates an mp3 file at the given path within self.srcdir. filepath is
given as an array of folder names, ending with the file name. Sets the
file's metadata from the provided dict. Returns the full, real path to
the file.
"""
realpath = os.path.join(self.srcdir, *filepath)
if not os.path.exists(os.path.dirname(realpath)):
os.makedirs(os.path.dirname(realpath))
realpath = os.path.join(self.srcdir, *filepath)
shutil.copy(os.path.join('rsrc', 'full.mp3'), realpath)
f = mediafile.MediaFile(realpath)
for attr in metadata:
setattr(f, attr, metadata[attr])
f.save()
return realpath

def test_import_copy_arrives(self):
track_names = ['The Opener', 'The Second Track', 'The Last Track']

for i, title in enumerate(track_names):
path = self.create_test_file(['the_album', 'track_%s.mp3' % (i+1)], {
'track': (i+1),
'artist': 'The Artist',
'album': 'The Album',
'title': title})

sources = [os.path.dirname(path)]

commands.import_files(
lib=self.lib,
paths=sources,
copy=True,
write=True,
autot=False,
logpath=None,
art=False,
threaded=False,
color=False,
delete=False,
quiet=True)

albums = self.lib.albums()
self.assertEqual(len(albums), 1)
self.assertEqual(albums[0].albumartist, 'The Artist')

artist_folder = os.path.join(self.libdir, 'The Artist')
album_folder = os.path.join(artist_folder, 'The Album')
self.assertEqual(len(os.listdir(artist_folder)), 1)
self.assertEqual(len(os.listdir(album_folder)), 3)

files = sorted(os.listdir(album_folder))
names = sorted(track_names)
for file, name in zip(files, names):
self.assertEqual(file, name + ".mp3")


class ListTest(unittest.TestCase):
def setUp(self):
self.io = _common.DummyIO()
Expand Down