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 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 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 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 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 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_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 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_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 write(self, path=None): """Write the item's metadata to a media file. Updates the mediafile with properties from itself. Can raise either a `ReadError` or a `WriteError`. """ if path is None: path = self.path else: path = normpath(path) try: mediafile = MediaFile(path) except (OSError, IOError) as exc: raise ReadError(self.path, exc) plugins.send('write', item=self, path=path) try: mediafile.update(self, id3v23=beets.config['id3v23'].get(bool)) 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_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_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 __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 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 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_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 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 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 __init__(self): super(ZeroPlugin, self).__init__() # Listeners. self.register_listener('write', self.write_event) self.register_listener('import_task_choice', self.import_task_choice_event) self.config.add({ 'fields': [], 'update_database': False, }) self.patterns = {} self.warned = False for field in self.config['fields'].as_str_seq(): if field in ('id', 'path', 'album_id'): self._log.warn(u'field \'{0}\' ignored, zeroing ' u'it would be dangerous', field) continue if field not in MediaFile.fields(): self._log.error(u'invalid field: {0}', field) continue try: self.patterns[field] = self.config[field].as_str_seq() except confit.NotFoundError: # Matches everything self.patterns[field] = True
def __init__(self): super(ZeroPlugin, self).__init__() # Listeners. self.register_listener("write", self.write_event) self.register_listener("import_task_choice", self.import_task_choice_event) self.config.add({"fields": [], "keep_fields": [], "update_database": False}) self.patterns = {} self.warned = False # We'll only handle `fields` or `keep_fields`, but not both. if self.config["fields"] and self.config["keep_fields"]: self._log.warn("cannot blacklist and whitelist at the same time") # Blacklist mode. if self.config["fields"]: self.validate_config("fields") for field in self.config["fields"].as_str_seq(): self.set_pattern(field) # Whitelist mode. elif self.config["keep_fields"]: self.validate_config("keep_fields") for field in MediaFile.fields(): if field in self.config["keep_fields"].as_str_seq(): continue self.set_pattern(field) # These fields should always be preserved. for key in ("id", "path", "album_id"): if key in self.patterns: del self.patterns[key]
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 _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 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 test_overwrite_full(self): mediafile = self._mediafile_fixture('full') tags = self._generate_tags() for key, value in tags.items(): setattr(mediafile, key, value) mediafile.save() # Make sure the tags are already set when writing a second time for key, value in tags.items(): setattr(mediafile, key, value) mediafile.save() mediafile = MediaFile(mediafile.path) self.assertTags(mediafile, tags)
def test_write_dates(self): mediafile = self._mediafile_fixture('full') mediafile.date = datetime.date(2001, 1, 2) mediafile.original_date = datetime.date(1999, 12, 30) mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.year, 2001) self.assertEqual(mediafile.month, 1) self.assertEqual(mediafile.day, 2) self.assertEqual(mediafile.date, datetime.date(2001, 1, 2)) self.assertEqual(mediafile.original_year, 1999) self.assertEqual(mediafile.original_month, 12) self.assertEqual(mediafile.original_day, 30) self.assertEqual(mediafile.original_date, datetime.date(1999, 12, 30))
def test_embed_non_image_file(self): album = self.add_album_fixture() logging.getLogger('beets.embedart').setLevel(logging.DEBUG) handle, tmp_path = tempfile.mkstemp() os.write(handle, 'I am not an image.') os.close(handle) try: self.run_command('embedart', '-f', tmp_path) finally: os.remove(tmp_path) mediafile = MediaFile(syspath(album.items()[0].path)) self.assertFalse(mediafile.images) # No image added.
def validate_config(self, mode): """Check whether fields in the configuration are valid. `mode` should either be "fields" or "keep_fields", indicating the section of the configuration to validate. """ for field in self.config[mode].as_str_seq(): if field not in MediaFile.fields(): self._log.error(u'invalid field: {0}', field) continue if mode == 'fields' and field in ('id', 'path', 'album_id'): self._log.warning( u'field \'{0}\' ignored, zeroing ' u'it would be dangerous', field) continue
def test_extended_field_write(self): plugin = BeetsPlugin() plugin.add_media_field('customtag', field_extension) try: mediafile = self._mediafile_fixture('empty') mediafile.customtag = u'F#' mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.customtag, u'F#') finally: delattr(MediaFile, 'customtag') Item._media_fields.remove('customtag')
def test_set_image_structure(self): mediafile = self._mediafile_fixture('empty') image = Image(data=self.png_data, desc=u'album cover', type=ImageType.front) mediafile.images = [image] mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(len(mediafile.images), 1) image = mediafile.images[0] self.assertEqual(image.data, self.png_data) self.assertEqual(image.mime_type, 'image/png') self.assertExtendedImageAttributes(image, desc=u'album cover', type=ImageType.front)
def test_cli_saves_track_gain(self): for item in self.lib.items(): self.assertIsNone(item.rg_track_peak) self.assertIsNone(item.rg_track_gain) mediafile = MediaFile(item.path) self.assertIsNone(mediafile.rg_track_peak) self.assertIsNone(mediafile.rg_track_gain) self.run_command('replaygain') # Skip the test if rg_track_peak and rg_track gain is None, assuming # that it could only happen if the decoder plugins are missing. if all(i.rg_track_peak is None and i.rg_track_gain is None for i in self.lib.items()): self.skipTest('decoder plugins could not be loaded.') for item in self.lib.items(): self.assertIsNotNone(item.rg_track_peak) self.assertIsNotNone(item.rg_track_gain) mediafile = MediaFile(item.path) self.assertAlmostEqual( mediafile.rg_track_peak, item.rg_track_peak, places=6) self.assertAlmostEqual( mediafile.rg_track_gain, item.rg_track_gain, places=2)
def test_no_fields(self): item = self.add_item_fixture(year=2016) item.write() mediafile = MediaFile(syspath(item.path)) self.assertEqual(mediafile.year, 2016) item_id = item.id self.load_plugins('zero') with control_stdin('y'): self.run_command('zero') item = self.lib.get_item(item_id) self.assertEqual(item['year'], 2016) self.assertEqual(mediafile.year, 2016)
def test_write_extended_tag_from_item(self): plugin = BeetsPlugin() plugin.add_media_field('customtag', field_extension) try: mediafile = self._mediafile_fixture('empty') self.assertIsNone(mediafile.customtag) item = Item(path=mediafile.path, customtag=u'Gb') item.write() mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.customtag, u'Gb') finally: delattr(MediaFile, 'customtag') Item._media_fields.remove('customtag')
def test_add_image_structure(self): mediafile = self._mediafile_fixture('image') self.assertEqual(len(mediafile.images), 2) image = Image(data=self.png_data, desc='the composer', type=ImageType.composer) mediafile.images += [image] mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(len(mediafile.images), 3) image = next( (i for i in mediafile.images if i.desc == 'the composer'), None) self.assertExtendedImageAttributes(image, desc='the composer', type=ImageType.composer)
def test_move_and_write_after_tags_changed(self): item = self.add_external_track('myexternal') old_path = self.get_path(item) self.assertIsFile(old_path) item['title'] = 'a new title' item.store() item.try_write() # Required to update mtime. self.runcli('alt', 'update', 'myexternal') item.load() new_path = self.get_path(item) self.assertIsNotFile(old_path) self.assertIsFile(new_path) mediafile = MediaFile(syspath(new_path)) self.assertEqual(mediafile.title, 'a new title')
def test_add_tiff_image(self): mediafile = self._mediafile_fixture('image') self.assertEqual(len(mediafile.images), 2) image = Image(data=self.tiff_data, desc='the composer', type=ImageType.composer) mediafile.images += [image] mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(len(mediafile.images), 3) # WMA does not preserve the order, so we have to work around this image = filter(lambda i: i.mime_type == 'image/tiff', mediafile.images)[0] self.assertExtendedImageAttributes(image, desc='the composer', type=ImageType.composer)
def read(self, read_path=None): """Read the metadata from the associated file. If read_path is specified, read metadata from that file instead. """ if read_path is None: read_path = self.path else: read_path = normpath(read_path) f = MediaFile(syspath(read_path)) for key in ITEM_KEYS_META: setattr(self, key, getattr(f, key)) self.path = read_path # Database's mtime should now reflect the on-disk value. if read_path == self.path: self.mtime = self.current_mtime()
def updateFormat(): myDB = db.DBConnection() tracks = myDB.select( 'SELECT * from tracks WHERE Location IS NOT NULL and Format IS NULL') if len(tracks) > 0: logger.info('Finding media format for %s files' % len(tracks)) for track in tracks: try: f = MediaFile(track['Location']) except Exception, e: logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) continue controlValueDict = {"TrackID": track['TrackID']} newValueDict = {"Format": f.format} myDB.upsert("tracks", newValueDict, controlValueDict) logger.info('Finished finding media format for %s files' % len(tracks))
def _set_pattern(self, field): """Populate `self.fields_to_progs` for a given field. Do some sanity checks then compile the regexes. """ if field not in MediaFile.fields(): self._log.error(u'invalid field: {0}', field) elif field in ('id', 'path', 'album_id'): self._log.warning( u'field \'{0}\' ignored, zeroing ' u'it would be dangerous', field) else: try: for pattern in self.config[field].as_str_seq(): prog = re.compile(pattern, re.IGNORECASE) self.fields_to_progs.setdefault(field, []).append(prog) except confit.NotFoundError: # Matches everything self.fields_to_progs[field] = []
def test_whitelist_and_blacklist(self): item = self.add_item_fixture(year=2016) item.write() mf = MediaFile(syspath(item.path)) self.assertEqual(mf.year, 2016) item_id = item.id self.config['zero']['fields'] = [u'year'] self.config['zero']['keep_fields'] = [u'comments'] self.load_plugins('zero') with control_stdin('y'): self.run_command('zero') item = self.lib.get_item(item_id) self.assertEqual(item['year'], 2016) self.assertEqual(mf.year, 2016)
def test_write_counters_without_total(self): mediafile = self._mediafile_fixture('full') self.assertEqual(mediafile.track, 2) self.assertEqual(mediafile.tracktotal, 3) self.assertEqual(mediafile.disc, 4) self.assertEqual(mediafile.disctotal, 5) mediafile.track = 10 delattr(mediafile, 'tracktotal') mediafile.disc = 10 delattr(mediafile, 'disctotal') mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.track, 10) self.assertEqual(mediafile.tracktotal, None) self.assertEqual(mediafile.disc, 10) self.assertEqual(mediafile.disctotal, None)
def test_no_patterns(self): self.config['zero']['fields'] = ['comments', 'month'] item = self.add_item_fixture( comments=u'test comment', title=u'Title', month=1, year=2000, ) item.write() self.load_plugins('zero') item.write() mf = MediaFile(syspath(item.path)) self.assertIsNone(mf.comments) self.assertIsNone(mf.month) self.assertEqual(mf.title, u'Title') self.assertEqual(mf.year, 2000)
def test_subcommand_query_exclude(self): item = self.add_item_fixture(year=2016, day=13, month=3, comments=u'test comment') item.write() self.config['zero']['fields'] = ['comments'] self.config['zero']['update_database'] = False self.config['zero']['auto'] = False self.load_plugins('zero') self.run_command('zero', 'year: 0000') mf = MediaFile(syspath(item.path)) self.assertEqual(mf.year, 2016) self.assertEqual(mf.comments, u'test comment')
def album_imported(self, lib, album): self.write_album = True print_("Tagging Replay Gain: %s - %s" % (album.albumartist, album.album)) try: media_files = [MediaFile(item.path) for item in album.items()] media_files = [mf for mf in media_files if self.requires_gain(mf)] #calculate gain. Return value - track_data: array dictionary indexed by filename track_data, album_data = rgcalc.calculate( [mf.path for mf in media_files], True, self.ref_level) for mf in media_files: self.write_gain(mf, track_data, album_data) except (FileTypeError, UnreadableFileError, TypeError, ValueError), e: log.error("failed to calculate replaygain: %s ", e)
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 __init__(self): super(ZeroPlugin, self).__init__() # Listeners. self.register_listener('write', self.write_event) self.register_listener('import_task_choice', self.import_task_choice_event) self.config.add({ 'fields': [], 'keep_fields': [], 'update_database': False, }) self.patterns = {} self.warned = False # We'll only handle `fields` or `keep_fields`, but not both. if self.config['fields'] and self.config['keep_fields']: self._log.warning( u'cannot blacklist and whitelist at the same time') # Blacklist mode. if self.config['fields']: self.validate_config('fields') for field in self.config['fields'].as_str_seq(): self.set_pattern(field) # Whitelist mode. elif self.config['keep_fields']: self.validate_config('keep_fields') for field in MediaFile.fields(): if field in self.config['keep_fields'].as_str_seq(): continue self.set_pattern(field) # These fields should always be preserved. for key in ('id', 'path', 'album_id'): if key in self.patterns: del self.patterns[key]
def _import_mtags(self, lib, opts, args): path, = args paths = [Path(path)] while paths: p = paths.pop(0) for child in p.iterdir(): if child.is_dir(): paths.append(child) continue loader = MTagLoader(child) al = [] for path, data in loader.items(): matching = lib.items(PathQuery('path', path)) if any(m.path == path.encode() for m in matching): continue print('add %r' % path) item = Item(path=path) mf = MediaFile(syspath(item.path)) for field in AUDIO_FIELDS: v = getattr(mf, field) item[field] = v values = {} for tag, converter in TAGS.items(): v = converter.get(data) if v is not None: values[tag] = v item[tag] = v for tag, converter in DEPENDENT_TAGS.items(): v = converter.get(data, values) if v is not None: item[tag] = v al.append(item) if al: #print(al) try: lib.add_album(al) except BaseException as e: import pdb pdb.post_mortem(e.__traceback__) return
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_add_image_structure(self): mediafile = self._mediafile_fixture('image') self.assertEqual(len(mediafile.images), 2) image = Image(data=self.png_data, desc='the composer', type=Image.TYPES.composer) mediafile.images += [image] mediafile.save() mediafile = MediaFile(mediafile.path) self.assertEqual(len(mediafile.images), 3) # WMA does not preserve the order, so we have to work around this try: image = filter(lambda i: i.desc == 'the composer', mediafile.images)[0] except IndexError: image = None self.assertExtendedImageAttributes(image, desc='the composer', type=Image.TYPES.composer)
def test_empty_query_n_response_no_changes(self): item = self.add_item_fixture(year=2016, day=13, month=3, comments=u'test comment') item.write() item_id = item.id self.config['zero']['fields'] = ['comments'] self.config['zero']['update_database'] = True self.config['zero']['auto'] = False self.load_plugins('zero') with control_stdin('n'): self.run_command('zero') mf = MediaFile(syspath(item.path)) item = self.lib.get_item(item_id) self.assertEqual(item['year'], 2016) self.assertEqual(mf.year, 2016) self.assertEqual(mf.comments, u'test comment') self.assertEqual(item['comments'], u'test comment')
def __init__(self): super(ZeroPlugin, self).__init__() self.register_listener('write', self.write_event) self.register_listener('import_task_choice', self.import_task_choice_event) self.config.add({ 'auto': True, 'fields': [], 'keep_fields': [], 'update_database': False, }) self.fields_to_progs = {} self.warned = False """Read the bulk of the config into `self.fields_to_progs`. After construction, `fields_to_progs` contains all the fields that should be zeroed as keys and maps each of those to a list of compiled regexes (progs) as values. A field is zeroed if its value matches one of the associated progs. If progs is empty, then the associated field is always zeroed. """ if self.config['fields'] and self.config['keep_fields']: self._log.warning( u'cannot blacklist and whitelist at the same time') # Blacklist mode. elif self.config['fields']: for field in self.config['fields'].as_str_seq(): self._set_pattern(field) # Whitelist mode. elif self.config['keep_fields']: for field in MediaFile.fields(): if (field not in self.config['keep_fields'].as_str_seq() and # These fields should always be preserved. field not in ('id', 'path', 'album_id')): self._set_pattern(field)