def create_importer(self, item_count=1, album_count=1): """Returns import session with fixtures. Copies the specified number of files to a subdirectory of ``self.temp_dir`` and creates a ``TestImportSession`` for this path. """ import_dir = os.path.join(self.temp_dir, 'import') if not os.path.isdir(import_dir): os.mkdir(import_dir) for i in range(album_count): album = u'album {0}'.format(i) album_dir = os.path.join(import_dir, album) os.mkdir(album_dir) for j in range(item_count): title = 'track {0}'.format(j) src = os.path.join(_common.RSRC, 'full.mp3') dest = os.path.join(album_dir, '{0}.mp3'.format(title)) shutil.copy(src, dest) mediafile = MediaFile(dest) mediafile.update({'title': title, 'album': album}) mediafile.save() config['import']['quiet'] = True config['import']['autotag'] = False config['import']['resume'] = False return TestImportSession(self.lib, logfile=None, query=None, paths=[import_dir])
def write(self, path=None): """Write the item's metadata to a media file. All fields in `_media_fields` are written to disk according to the values on this object. Can raise either a `ReadError` or a `WriteError`. """ if path is None: path = self.path else: path = normpath(path) tags = dict(self) plugins.send('write', item=self, path=path, tags=tags) try: mediafile = MediaFile(syspath(path), id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, UnreadableFileError) as exc: raise ReadError(self.path, exc) mediafile.update(tags) try: mediafile.save() except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) # The file has a new mtime. if path == self.path: self.mtime = self.current_mtime() plugins.send('after_write', item=self, path=path)
def write(self, path=None): """Write the item's metadata to a media file. ``path`` defaults to the item's path property. Can raise either a `ReadError` or a `WriteError`. """ if path is None: path = self.path else: path = normpath(path) try: f = MediaFile(syspath(path)) except (OSError, IOError) as exc: raise ReadError(self.path, exc) plugins.send('write', item=self, path=path) for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) try: f.save(id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) # The file has a new mtime. self.mtime = self.current_mtime() plugins.send('after_write', item=self)
def test_modified_metadata_detected(self): mf = MediaFile(self.i.path) mf.title = u'differentTitle' mf.save() self._update() item = self.lib.items().get() self.assertEqual(item.title, u'differentTitle')
def test_modified_metadata_moved(self): mf = MediaFile(self.i.path) mf.title = u'differentTitle' mf.save() self._update(move=True) item = self.lib.items().get() self.assertTrue(b'differentTitle' in item.path)
def write(self): """Writes the item's metadata to the associated file. """ f = MediaFile(_syspath(self.path)) for key in ITEM_KEYS_WRITABLE: setattr(f, key, getattr(self, key)) f.save()
def test_modified_album_metadata_moved(self): mf = MediaFile(self.i.path) mf.album = u'differentAlbum' mf.save() self._update(move=True) item = self.lib.items().get() self.assertTrue(u'differentAlbum' in item.path)
def test_modified_metadata_not_moved(self): mf = MediaFile(self.i.path) mf.title = u'differentTitle' mf.save() self._update(move=False) item = self.lib.items().get() self.assertTrue(u'differentTitle' not in item.path)
def create_mediafile_fixture(self, ext='mp3', images=[]): """Copies a fixture mediafile with the extension to a temporary location and returns the path. It keeps track of the created locations and will delete the with `remove_mediafile_fixtures()` `images` is a subset of 'png', 'jpg', and 'tiff'. For each specified extension a cover art image is added to the media file. """ src = os.path.join(_common.RSRC, 'full.' + ext) handle, path = mkstemp() os.close(handle) shutil.copyfile(src, path) if images: mediafile = MediaFile(path) imgs = [] for img_ext in images: img_path = os.path.join(_common.RSRC, 'image-2x3.{0}'.format(img_ext)) with open(img_path, 'rb') as f: imgs.append(Image(f.read())) mediafile.images = imgs mediafile.save() if not hasattr(self, '_mediafile_fixtures'): self._mediafile_fixtures = [] self._mediafile_fixtures.append(path) return path
def create_mediafile_fixture(self, ext='mp3', images=[]): """Copies a fixture mediafile with the extension to a temporary location and returns the path. It keeps track of the created locations and will delete the with `remove_mediafile_fixtures()` `images` is a subset of 'png', 'jpg', and 'tiff'. For each specified extension a cover art image is added to the media file. """ src = os.path.join(_common.RSRC, util.bytestring_path('full.' + ext)) handle, path = mkstemp() os.close(handle) shutil.copyfile(src, path) if images: mediafile = MediaFile(path) imgs = [] for img_ext in images: file = util.bytestring_path('image-2x3.{0}'.format(img_ext)) img_path = os.path.join(_common.RSRC, file) with open(img_path, 'rb') as f: imgs.append(Image(f.read())) mediafile.images = imgs mediafile.save() if not hasattr(self, '_mediafile_fixtures'): self._mediafile_fixtures = [] self._mediafile_fixtures.append(path) return path
def embedLyrics(downloaded_track_list): logger.info('Adding lyrics') # TODO: If adding lyrics for flac & lossy, only fetch the lyrics once # and apply it to both files for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.error('Could not read %s. Not checking lyrics', downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) continue if f.albumartist and f.title: metalyrics = lyrics.getLyrics(f.albumartist, f.title) elif f.artist and f.title: metalyrics = lyrics.getLyrics(f.artist, f.title) else: logger.info('No artist/track metadata found for track: %s. Not fetching lyrics', downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) metalyrics = None if lyrics: logger.debug('Adding lyrics to: %s', downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) f.lyrics = metalyrics try: f.save() except: logger.error('Cannot save lyrics to: %s. Skipping', downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) continue
def test_modified_metadata_not_moved(self): mf = MediaFile(self.i.path) mf.title = 'differentTitle' mf.save() self._update(move=False) item = self.lib.items().next() self.assertTrue('differentTitle' not in item.path)
def test_modified_metadata_detected(self): mf = MediaFile(self.i.path) mf.title = 'differentTitle' mf.save() self._update() item = self.lib.items().next() self.assertEqual(item.title, 'differentTitle')
def test_modified_album_metadata_moved(self): mf = MediaFile(self.i.path) mf.album = 'differentAlbum' mf.save() self._update(move=True) item = self.lib.items().next() self.assertTrue('differentAlbum' in item.path)
def test_modified_metadata_moved(self): mf = MediaFile(self.i.path) mf.title = 'differentTitle' mf.save() self._update(move=True) item = self.lib.items().next() self.assertTrue('differentTitle' in item.path)
def write(self): """Writes the item's metadata to the associated file. """ f = MediaFile(syspath(self.path)) for key in ITEM_KEYS_WRITABLE: setattr(f, key, getattr(self, key)) f.save()
def test_modified_album_metadata_art_moved(self): artpath = self.album.artpath mf = MediaFile(self.i.path) mf.album = 'differentAlbum' mf.save() self._update(move=True) album = self.lib.albums()[0] self.assertNotEqual(artpath, album.artpath)
def test_modified_album_metadata_art_moved(self): artpath = self.album.artpath mf = MediaFile(self.i.path) mf.album = u'differentAlbum' mf.save() self._update(move=True) album = self.lib.albums()[0] self.assertNotEqual(artpath, album.artpath)
def test_selective_modified_metadata_moved(self): mf = MediaFile(syspath(self.i.path)) mf.title = u'differentTitle' mf.genre = u'differentGenre' mf.save() self._update(move=True, fields=['title']) item = self.lib.items().get() self.assertTrue(b'differentTitle' in item.path) self.assertNotEqual(item.genre, u'differentGenre')
def test_selective_modified_album_metadata_not_moved(self): mf = MediaFile(syspath(self.i.path)) mf.album = u'differentAlbum' mf.genre = u'differentGenre' mf.save() self._update(move=True, fields=['genre']) item = self.lib.items().get() self.assertTrue(b'differentAlbum' not in item.path) self.assertEqual(item.genre, u'differentGenre')
def __copy_file(self, dest_path, metadata): # Copy files resource_path = os.path.join(RSRC, b'full.mp3') shutil.copy(resource_path, dest_path) medium = MediaFile(dest_path) # Set metadata for attr in metadata: setattr(medium, attr, metadata[attr]) medium.save()
def test_asis_updates_metadata(self): self.setup_importer.run() medium = MediaFile(self.lib.items().get().path) medium.title = 'New Title' medium.save() self.importer.add_choice(importer.action.ASIS) self.importer.run() self.assertEqual(self.lib.items().get().title, 'New Title')
def __copy_file(self, dest_path, metadata): # Copy files resource_path = os.path.join(RSRC, 'full.mp3') shutil.copy(resource_path, dest_path) medium = MediaFile(dest_path) # Set metadata for attr in metadata: setattr(medium, attr, metadata[attr]) medium.save()
def write(self): """Writes the item's metadata to the associated file. """ f = MediaFile(syspath(self.path)) for key in ITEM_KEYS_WRITABLE: setattr(f, key, getattr(self, key)) f.save() # The file has a new mtime. self.mtime = self.current_mtime()
def test_mtime_match_skips_update(self): mf = MediaFile(self.i.path) mf.title = 'differentTitle' mf.save() # Make in-memory mtime match on-disk mtime. self.i.mtime = os.path.getmtime(self.i.path) self.lib.store(self.i) self._update(reset_mtime=False) item = self.lib.items().next() self.assertEqual(item.title, 'full')
def test_mtime_match_skips_update(self): mf = MediaFile(self.i.path) mf.title = u'differentTitle' mf.save() # Make in-memory mtime match on-disk mtime. self.i.mtime = os.path.getmtime(self.i.path) self.i.store() self._update(reset_mtime=False) item = self.lib.items().get() self.assertEqual(item.title, u'full')
def test_asis_updated_moves_file(self): self.setup_importer.run() medium = MediaFile(self.lib.items().get().path) medium.title = "New Title" medium.save() old_path = os.path.join("Applied Artist", "Applied Album", "Applied Title 1.mp3") self.assert_file_in_lib(old_path) self.importer.add_choice(importer.action.ASIS) self.importer.run() self.assert_file_in_lib("Applied Artist", "Applied Album", "New Title.mp3") self.assert_file_not_in_lib(old_path)
def test_delete_art(self): mediafile = self._mediafile_fixture('empty') mediafile.art = self.jpg_data mediafile.save() mediafile = MediaFile(mediafile.path) self.assertIsNotNone(mediafile.art) del mediafile.art mediafile.save() mediafile = MediaFile(mediafile.path) self.assertIsNone(mediafile.art)
def create_importer(self, item_count=1, album_count=1): """Create files to import and return corresponding session. Copies the specified number of files to a subdirectory of `self.temp_dir` and creates a `TestImportSession` for this path. """ import_dir = os.path.join(self.temp_dir, b'import') if not os.path.isdir(import_dir): os.mkdir(import_dir) album_no = 0 while album_count: album = util.bytestring_path(u'album {0}'.format(album_no)) album_dir = os.path.join(import_dir, album) if os.path.exists(album_dir): album_no += 1 continue os.mkdir(album_dir) album_count -= 1 track_no = 0 album_item_count = item_count while album_item_count: title = u'track {0}'.format(track_no) src = os.path.join(_common.RSRC, b'full.mp3') title_file = util.bytestring_path('{0}.mp3'.format(title)) dest = os.path.join(album_dir, title_file) if os.path.exists(dest): track_no += 1 continue album_item_count -= 1 shutil.copy(src, dest) mediafile = MediaFile(dest) mediafile.update({ 'artist': 'artist', 'albumartist': 'album artist', 'title': title, 'album': album, 'mb_albumid': None, 'mb_trackid': None, }) mediafile.save() config['import']['quiet'] = True config['import']['autotag'] = False config['import']['resume'] = False return TestImportSession(self.lib, loghandler=None, query=None, paths=[import_dir])
def test_convert_write_tags(self): item = self.add_track(myexternal='true', format='m4a', title=u'TITLE') # We "convert" by copying the file. Setting the title simulates # a badly behaved converter mediafile_converted = MediaFile(syspath(item.path)) mediafile_converted.title = u'WRONG' mediafile_converted.save() self.runcli('alt', 'update', 'myexternal') item.load() alt_mediafile = MediaFile(syspath(self.get_path(item))) self.assertEqual(alt_mediafile.title, u'TITLE')
def test_asis_updated_moves_file(self): self.setup_importer.run() medium = MediaFile(self.lib.items().get().path) medium.title = 'New Title' medium.save() old_path = os.path.join('Applied Artist', 'Applied Album', 'Applied Title 1.mp3') self.assert_file_in_lib(old_path) self.importer.add_choice(importer.action.ASIS) self.importer.run() self.assert_file_in_lib('Applied Artist', 'Applied Album', 'New Title.mp3') self.assert_file_not_in_lib(old_path)
def test_path(self): path = self.create_mediafile_fixture() mediafile = MediaFile(path) mediafile.albumartist = 'AAA' mediafile.disctitle = 'DDD' mediafile.genres = ['a', 'b', 'c'] mediafile.composer = None mediafile.save() out = self.run_with_output(path) self.assertIn(path, out) self.assertIn('albumartist: AAA', out) self.assertIn('disctitle: DDD', out) self.assertIn('genres: a; b; c', out) self.assertNotIn('composer:', out)
def test_asis_updated_without_copy_does_not_move_file(self): self.setup_importer.run() medium = MediaFile(self.lib.items().get().path) medium.title = 'New Title' medium.save() old_path = os.path.join('Applied Artist', 'Applied Album', 'Applied Title 1.mp3') self.assert_file_in_lib(old_path) config['import']['copy'] = False self.importer.add_choice(importer.action.ASIS) self.importer.run() self.assert_file_not_in_lib('Applied Artist', 'Applied Album', 'New Title.mp3') self.assert_file_in_lib(old_path)
def write(self, path=None, tags=None): """Write the item's metadata to a media file. All fields in `_media_fields` are written to disk according to the values on this object. `path` is the path of the mediafile to write the data to. It defaults to the item's path. `tags` is a dictionary of additional metadata the should be written to the file. (These tags need not be in `_media_fields`.) Can raise either a `ReadError` or a `WriteError`. """ if path is None: path = self.path else: path = normpath(path) # Get the data to write to the file. item_tags = dict(self) item_tags = { k: v for k, v in item_tags.items() if k in self._media_fields } # Only write media fields. if tags is not None: item_tags.update(tags) plugins.send('write', item=self, path=path, tags=item_tags) # Open the file. try: mediafile = MediaFile(syspath(path), id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, UnreadableFileError) as exc: raise ReadError(self.path, exc) # Write the tags to the file. mediafile.update(item_tags) try: mediafile.save() except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) # The file has a new mtime. if path == self.path: self.mtime = self.current_mtime() plugins.send('after_write', item=self, path=path)
def test_path(self): path = self.create_mediafile_fixture() mediafile = MediaFile(path) mediafile.albumartist = "AAA" mediafile.disctitle = "DDD" mediafile.genres = ["a", "b", "c"] mediafile.composer = None mediafile.save() out = self.run_with_output("info", path) self.assertIn(path, out) self.assertIn("albumartist: AAA", out) self.assertIn("disctitle: DDD", out) self.assertIn("genres: a; b; c", out) self.assertNotIn("composer:", out) self.remove_mediafile_fixtures()
def _create_import_dir(self, count=3): """Creates a directory with media files to import. Sets ``self.import_dir`` to the path of the directory. Also sets ``self.import_media`` to a list :class:`MediaFile` for all the files in the directory. The directory has following layout the_album/ track_1.mp3 track_2.mp3 track_3.mp3 :param count: Number of files to create """ self.import_dir = os.path.join(self.temp_dir, 'testsrcdir') if os.path.isdir(self.import_dir): shutil.rmtree(self.import_dir) album_path = os.path.join(self.import_dir, 'the_album') os.makedirs(album_path) resource_path = os.path.join(_common.RSRC, 'full.mp3') metadata = { 'artist': 'Tag Artist', 'album': 'Tag Album', 'albumartist': None, 'mb_trackid': None, 'mb_albumid': None, 'comp': None } self.media_files = [] for i in range(count): # Copy files medium_path = os.path.join(album_path, 'track_%d.mp3' % (i + 1)) shutil.copy(resource_path, medium_path) medium = MediaFile(medium_path) # Set metadata metadata['track'] = i + 1 metadata['title'] = 'Tag Title %d' % (i + 1) for attr in metadata: setattr(medium, attr, metadata[attr]) medium.save() self.media_files.append(medium) self.import_media = self.media_files
def write(self, path=None, tags=None): """Write the item's metadata to a media file. All fields in `_media_fields` are written to disk according to the values on this object. `path` is the path of the mediafile to write the data to. It defaults to the item's path. `tags` is a dictionary of additional metadata the should be written to the file. (These tags need not be in `_media_fields`.) Can raise either a `ReadError` or a `WriteError`. """ if path is None: path = self.path else: path = normpath(path) # Get the data to write to the file. item_tags = dict(self) item_tags = {k: v for k, v in item_tags.items() if k in self._media_fields} # Only write media fields. if tags is not None: item_tags.update(tags) plugins.send('write', item=self, path=path, tags=item_tags) # Open the file. try: mediafile = MediaFile(syspath(path), id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, UnreadableFileError) as exc: raise ReadError(self.path, exc) # Write the tags to the file. mediafile.update(item_tags) try: mediafile.save() except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) # The file has a new mtime. if path == self.path: self.mtime = self.current_mtime() plugins.send('after_write', item=self, path=path)
def test_delete_partial_date(self): mediafile = self._mediafile_fixture('empty') mediafile.date = datetime.date(2001, 12, 3) mediafile.save() mediafile = MediaFile(mediafile.path) self.assertIsNotNone(mediafile.date) self.assertIsNotNone(mediafile.year) self.assertIsNotNone(mediafile.month) self.assertIsNotNone(mediafile.day) delattr(mediafile, 'month') mediafile.save() mediafile = MediaFile(mediafile.path) self.assertIsNotNone(mediafile.date) self.assertIsNotNone(mediafile.year) self.assertIsNone(mediafile.month) self.assertIsNone(mediafile.day)
def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.error(u'Could not read %s. Not adding album art' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) continue logger.debug('Adding album art to: %s' % downloaded_track) try: f.art = artwork f.save() except Exception, e: logger.error(u'Error ebedding album art to: %s. Error: %s' % (downloaded_track.decode(headphones.SYS_ENCODING, 'replace'), str(e))) continue
def test_collect_item_and_path(self): path = self.create_mediafile_fixture() mediafile = MediaFile(path) item, = self.add_item_fixtures() item.album = mediafile.album = 'AAA' item.tracktotal = mediafile.tracktotal = 5 item.title = 'TTT' mediafile.title = 'SSS' item.write() item.store() mediafile.save() out = self.run_with_output('--summarize', 'album:AAA', path) self.assertIn('album: AAA', out) self.assertIn('tracktotal: 5', out) self.assertIn('title: [various]', out)
def test_collect_item_and_path(self): path = self.create_mediafile_fixture() mediafile = MediaFile(path) item, = self.add_item_fixtures() item.album = mediafile.album = "AAA" item.tracktotal = mediafile.tracktotal = 5 item.title = "TTT" mediafile.title = "SSS" item.write() item.store() mediafile.save() out = self.run_with_output("info", "--summarize", "album:AAA", path) self.assertIn(u"album: AAA", out) self.assertIn(u"tracktotal: 5", out) self.assertIn(u"title: [various]", out) self.remove_mediafile_fixtures()
def create_importer(self, item_count=1, album_count=1): """Returns import session with fixtures. Copies the specified number of files to a subdirectory of ``self.temp_dir`` and creates a ``TestImportSession`` for this path. """ import_dir = os.path.join(self.temp_dir, 'import') if not os.path.isdir(import_dir): os.mkdir(import_dir) for i in range(album_count): album = u'album {0}'.format(i) album_dir = os.path.join(import_dir, album) os.mkdir(album_dir) for j in range(item_count): title = 'track {0}'.format(j) src = os.path.join(_common.RSRC, 'full.mp3') dest = os.path.join(album_dir, '{0}.mp3'.format(title)) shutil.copy(src, dest) mediafile = MediaFile(dest) mediafile.update({ 'artist': 'artist', 'albumartist': 'album artist', 'title': title, 'album': album, 'mb_albumid': None, 'mb_trackid': None, }) mediafile.save() config['import']['quiet'] = True config['import']['autotag'] = False config['import']['resume'] = False return TestImportSession(self.lib, logfile=None, query=None, paths=[import_dir])
def write(self): """Write the item's metadata to the associated file. Can raise either a `ReadError` or a `WriteError`. """ try: f = MediaFile(syspath(self.path)) except (OSError, IOError) as exc: raise ReadError(self.path, exc) plugins.send('write', item=self) for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) try: f.save(id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) # The file has a new mtime. self.mtime = self.current_mtime() plugins.send('after_write', item=self)
def write(self): """Writes the item's metadata to the associated file. """ plugins.send('write', item=self) try: f = MediaFile(syspath(self.path)) except (OSError, IOError) as exc: raise util.FilesystemError(exc, 'read', (self.path, ), traceback.format_exc()) for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) try: f.save(id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError) as exc: raise util.FilesystemError(exc, 'write', (self.path, ), traceback.format_exc()) # The file has a new mtime. self.mtime = self.current_mtime()
def write(self): """Writes the item's metadata to the associated file. """ plugins.send('write', item=self) try: f = MediaFile(syspath(self.path)) except (OSError, IOError) as exc: raise util.FilesystemError(exc, 'read', (self.path,), traceback.format_exc()) for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) try: f.save(id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError) as exc: raise util.FilesystemError(exc, 'write', (self.path,), traceback.format_exc()) # The file has a new mtime. self.mtime = self.current_mtime()
def modifyFile(self, path, title='a different title'): mediafile = MediaFile(path) mediafile.title = title mediafile.save()