class ImportfeedsTestTest(unittest.TestCase): def setUp(self): config.clear() config.read(user=False) self.importfeeds = ImportFeedsPlugin() self.lib = Library(':memory:') self.feeds_dir = tempfile.mkdtemp() config['importfeeds']['dir'] = self.feeds_dir def tearDown(self): shutil.rmtree(self.feeds_dir) def test_multi_format_album_playlist(self): config['importfeeds']['formats'] = 'm3u_multi' album = Album(album='album/name', id=1) item_path = os.path.join('path', 'to', 'item') item = Item(title='song', album_id=1, path=item_path) self.lib.add(album) self.lib.add(item) self.importfeeds.album_imported(self.lib, album) playlist_path = os.path.join(self.feeds_dir, os.listdir(self.feeds_dir)[0]) self.assertTrue(playlist_path.endswith('album_name.m3u')) with open(playlist_path) as playlist: self.assertIn(item_path, playlist.read())
def setUp(self): config.clear() config.read(user=False) self.importfeeds = ImportFeedsPlugin() self.lib = Library(':memory:') self.feeds_dir = tempfile.mkdtemp() config['importfeeds']['dir'] = self.feeds_dir
class IntQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(':memory:') def test_exact_value_match(self): item = self.add_item(bpm=120) matched = self.lib.items('bpm:120').get() self.assertEqual(item.id, matched.id) def test_range_match(self): item = self.add_item(bpm=120) self.add_item(bpm=130) matched = self.lib.items('bpm:110..125') self.assertEqual(1, len(matched)) self.assertEqual(item.id, matched.get().id) def test_flex_range_match(self): Item._types = {'myint': types.Integer()} item = self.add_item(myint=2) matched = self.lib.items('myint:2').get() self.assertEqual(item.id, matched.id) def test_flex_dont_match_missing(self): Item._types = {'myint': types.Integer()} self.add_item() matched = self.lib.items('myint:2').get() self.assertIsNone(matched) def test_no_substring_match(self): self.add_item(bpm=120) matched = self.lib.items('bpm:12').get() self.assertIsNone(matched)
def prompt_tags(db_file, query): library = Library(db_file) for track in library.items(query=query): try: scan_version = int(float(track.scan_version)) except Exception: scan_version = 0 if scan_version < VERSION: _prompt_for_track(track, TAGS_MODEL) track.scan_version = VERSION track.store()
def get_tracks(db_file, tag_list: List[str]) -> Dict[str, Track]: result = dict() library = Library(db_file) tag_list = [ tag for tag in tag_list if tag != "rating" and not tag.startswith("_") ] for item in library.items(): path = convert_attr_to_string(item.path).lower() tags = {tag: _get_attr_dont_throw(item, tag) for tag in tag_list} rating = _get_int_attr_dont_throw(item, "rating") if not (rating is not None and 0 <= rating <= 5): rating = None result[path] = Track(Path(path), tags, rating) return result
def create(self, lib: Library, name: str, qs: str, playlist_dir: str, relative_to: str): if playlist_dir is None: playlist_dir = self.config_playlist_dir() if relative_to is None: relative_to = self.config_relative_to() # Try to parse the query try: if qs is None: query, sort = None, None else: query, sort = parse_query_string(qs, Item) except ParsingError as ex: self._log.warning(u'invalid query: {}', ex) return # Map items to their paths items = lib.items(query, sort) item_path: Callable[[Item], str] = lambda item: path.relpath( item.path.decode('utf-8'), relative_to) paths = [item_path(item) for item in items] filename = path.join(playlist_dir, name + '.m3u') with open(filename, 'w+') as file: write_str = '\n'.join(paths) file.write(write_str)
def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = util.py3_path(self.temp_dir) self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = 1 self.config['ui']['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, b'libdir') os.mkdir(self.libdir) self.config['directory'] = util.py3_path(self.libdir) if disk: dbpath = util.bytestring_path( self.config['library'].as_filename() ) else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir)
class NoneQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(':memory:') def test_match_singletons(self): singleton = self.add_item() album_item = self.add_album().items().get() matched = self.lib.items(NoneQuery('album_id')) self.assertInResult(singleton, matched) self.assertNotInResult(album_item, matched) def test_match_after_set_none(self): item = self.add_item(rg_track_gain=0) matched = self.lib.items(NoneQuery('rg_track_gain')) self.assertNotInResult(item, matched) item['rg_track_gain'] = None item.store() matched = self.lib.items(NoneQuery('rg_track_gain')) self.assertInResult(item, matched)
class NoneQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(":memory:") def test_match_singletons(self): singleton = self.add_item() album_item = self.add_album().items().get() matched = self.lib.items(NoneQuery("album_id")) self.assertInResult(singleton, matched) self.assertNotInResult(album_item, matched) def test_match_after_set_none(self): item = self.add_item(rg_track_gain=0) matched = self.lib.items(NoneQuery("rg_track_gain")) self.assertNotInResult(item, matched) item["rg_track_gain"] = None item.store() matched = self.lib.items(NoneQuery("rg_track_gain")) self.assertInResult(item, matched)
def test_edit_invalid_config_file(self): self.lib = Library(':memory:') with open(self.config_path, 'w') as file: file.write('invalid: [') config.clear() config._materialized = False os.environ['EDITOR'] = 'myeditor' with patch('os.execlp') as execlp: self.run_command('config', '-e') execlp.assert_called_once_with('myeditor', 'myeditor', self.config_path)
class BoolQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(':memory:') Item._types = {'flexbool': types.Boolean()} def tearDown(self): Item._types = {} def test_parse_true(self): item_true = self.add_item(comp=True) item_false = self.add_item(comp=False) matched = self.lib.items('comp:true') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_true(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:true') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_false(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:false') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_1(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:1') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_0(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:0') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_any_string(self): # TODO this should be the other way around item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:something') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def assertInResult(self, item, results): result_ids = map(lambda i: i.id, results) self.assertIn(item.id, result_ids) def assertNotInResult(self, item, results): result_ids = map(lambda i: i.id, results) self.assertNotIn(item.id, result_ids)
class IntQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(':memory:') def tearDown(self): Item._types = {} def test_exact_value_match(self): item = self.add_item(bpm=120) matched = self.lib.items('bpm:120').get() self.assertEqual(item.id, matched.id) def test_range_match(self): item = self.add_item(bpm=120) self.add_item(bpm=130) matched = self.lib.items('bpm:110..125') self.assertEqual(1, len(matched)) self.assertEqual(item.id, matched.get().id) def test_flex_range_match(self): Item._types = {'myint': types.Integer()} item = self.add_item(myint=2) matched = self.lib.items('myint:2').get() self.assertEqual(item.id, matched.id) def test_flex_dont_match_missing(self): Item._types = {'myint': types.Integer()} self.add_item() matched = self.lib.items('myint:2').get() self.assertIsNone(matched) def test_no_substring_match(self): self.add_item(bpm=120) matched = self.lib.items('bpm:12').get() self.assertIsNone(matched)
def write_tracks_rating_and_tags(db_file, tracks: Dict[str, Track]): library = Library(db_file) conflicting_tracks = [] traktor_modification_count = 0 for i in library.items(): path = convert_attr_to_string(i.path).lower() if path in tracks: if tracks.get(path).rating is not None: i.rating = tracks.get(path).rating conflicting_tags = [] for tag_key, traktor_tag_value in tracks.get(path).tags.items(): existing_tag_in_beets = _get_attr_dont_throw(i, tag_key) if existing_tag_in_beets is None or existing_tag_in_beets != traktor_tag_value: traktor_modification_count += 1 setattr(i, tag_key, traktor_tag_value) if existing_tag_in_beets is not None and existing_tag_in_beets != traktor_tag_value: conflicting_tags.append( (tag_key, existing_tag_in_beets, traktor_tag_value)) if conflicting_tags: conflicting_tracks.append((path, conflicting_tags)) i.store() if conflicting_tracks: for c in conflicting_tracks[:10]: print( "Conflict in '%s':\n Conflicting tags (tag_key, beet_tags, traktor_tags):\n\t%s" % (c[0], c[1])) print("==========================") print( "Conflicting tags were overwritten in beets: Traktor tags have priority over beets" ) print("Total conflicting tracks: %s" % len(conflicting_tracks)) print("New tags coming from Traktor: %s" % traktor_modification_count)
def ls(lib: Library, opts, args): items = lib.items(" ".join(args)) item: Item for item in items: path = item.destination().decode("utf-8") flac: bool = any(ext in path for ext in ["flac", "alac", "wav"]) bit_depth = item.bitdepth sample_rate = round(float(item["samplerate"]) / 1000, 1) if flac: print( f"{item} - {'FLAC' if flac else path[path.rfind('.')+1:].upper()} - {sample_rate}/{bit_depth}" ) else: print( f"{item} - {path[path.rfind('.')+1:].upper()} - {item.bitrate//1000}kbps" )
class BoolQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(':memory:') Item._types = {'flexbool': types.Boolean()} def tearDown(self): Item._types = {} def test_parse_true(self): item_true = self.add_item(comp=True) item_false = self.add_item(comp=False) matched = self.lib.items('comp:true') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_true(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:true') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_false(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:false') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_1(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:1') self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_0(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:0') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_any_string(self): # TODO this should be the other way around item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items('flexbool:something') self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched)
class BoolQueryTest(unittest.TestCase, TestHelper): def setUp(self): self.lib = Library(":memory:") Item._types = {"flexbool": types.Boolean()} def tearDown(self): Item._types = {} def test_parse_true(self): item_true = self.add_item(comp=True) item_false = self.add_item(comp=False) matched = self.lib.items("comp:true") self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_true(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items("flexbool:true") self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_false(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items("flexbool:false") self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_1(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items("flexbool:1") self.assertInResult(item_true, matched) self.assertNotInResult(item_false, matched) def test_flex_parse_0(self): item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items("flexbool:0") self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) def test_flex_parse_any_string(self): # TODO this should be the other way around item_true = self.add_item(flexbool=True) item_false = self.add_item(flexbool=False) matched = self.lib.items("flexbool:something") self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched)
def setUp(self): self.lib = Library(':memory:') self.temp_dir = mkdtemp() if 'EDITOR' in os.environ: del os.environ['EDITOR'] os.environ['BEETSDIR'] = self.temp_dir self.config_path = os.path.join(self.temp_dir, 'config.yaml') with open(self.config_path, 'w') as file: file.write('library: lib\n') file.write('option: value\n') file.write('password: password_value') self.cli_config_path = os.path.join(self.temp_dir, 'cli_config.yaml') with open(self.cli_config_path, 'w') as file: file.write('option: cli overwrite') config.clear() config['password'].redact = True config._materialized = False
def setUp(self): self.lib = Library(":memory:") Item._types = {"flexbool": types.Boolean()}
def setUp(self): self.lib = Library(':memory:')
def setUp(self): self.lib = Library(':memory:') Item._types = {'flexbool': types.Boolean()}
def setUp(self): self.lib = Library(":memory:")
class TestHelper(object): """Helper mixin for high-level cli and plugin tests. This mixin provides methods to isolate beets' global state provide fixtures. """ # TODO automate teardown through hook registration def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = self.temp_dir self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = True self.config['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, 'libdir') os.mkdir(self.libdir) self.config['directory'] = self.libdir if disk: dbpath = self.config['library'].as_filename() else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir) def teardown_beets(self): self.lib._connection().close() del os.environ['BEETSDIR'] # FIXME somehow close all open fd to the ilbrary self.remove_temp_dir() self.config.clear() beets.config.read(user=False, defaults=True) def load_plugins(self, *plugins): """Load and initialize plugins by names. Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ beets.config['plugins'] = [] for plugin in beets.plugins._classes: plugin.listeners = None beets.plugins._classes = set() beets.plugins._instances = {} def create_importer(self, file_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(file_count): title = 'track {0}'.format(i) src = os.path.join(_common.RSRC, 'full.mp3') dest = os.path.join(import_dir, '{0}.mp3'.format(title)) shutil.copy(src, dest) config['import']['quiet'] = True config['import']['autotag'] = False config['import']['resume'] = False return TestImportSession(self.lib, logfile=None, query=None, paths=[import_dir]) def add_item_fixtures(self, ext='mp3', count=1): items = [] paths = glob(os.path.join(_common.RSRC, '*.' + ext)) for path in paths[0:count]: item = Item.from_path(str(path)) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return items def create_mediafile_fixture(self, ext='mp3'): """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()` """ src = os.path.join(_common.RSRC, 'full.' + ext) handle, path = mkstemp() os.close(handle) shutil.copyfile(src, path) if not hasattr(self, '_mediafile_fixtures'): self._mediafile_fixtures = [] self._mediafile_fixtures.append(path) return path def remove_mediafile_fixtures(self): if hasattr(self, '_mediafile_fixtures'): for path in self._mediafile_fixtures: os.remove(path) def run_command(self, *args): if hasattr(self, 'lib'): lib = self.lib else: lib = Library(':memory:') beets.ui._raw_main(list(args), lib) def create_temp_dir(self): """Create a temporary directory and assign it into `self.temp_dir`. Call `remove_temp_dir` later to delete it. """ self.temp_dir = mkdtemp() def remove_temp_dir(self): """Delete the temporary directory created by `create_temp_dir`. """ shutil.rmtree(self.temp_dir)
gitAnnexLib.from_backup(beetsCommands.get_library()) return redirect('/albums') @app.route("/artists") def artysci(): return render_template('artists.html') @app.route("/songs") def muzyka(): return render_template('songs.html') local_repo = gitAnnexLib.Repo(path=beetsCommands.get_library(), local=1) lib = Library(beetsCommands.get_database()) local_repo.annex_direct() get_backup(local_repo.path) import_to_beets(local_repo.path, first=1) get_backup(local_repo.path) local_repo.annex_indirect() #print("local repo autopushing sdsdfsdfdsfdsfsdfs") #print(local_repo.autopushing) for autopush in local_repo.autopushing: autopush.get_from(local_repo) #print("local repo autogetting sdsdfsdfdsfdsfsdfs") #print(local_repo.autogetting) for autoget in local_repo.autogetting: local_repo.get_from(autoget) if __name__ == "__main__":
def run_command(self, *args): if hasattr(self, 'lib'): lib = self.lib else: lib = Library(':memory:') beets.ui._raw_main(list(args), lib)
class TestHelper(object): """Helper mixin for high-level cli and plugin tests. This mixin provides methods to isolate beets' global state provide fixtures. """ # TODO automate teardown through hook registration def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = self.temp_dir self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = 1 self.config['ui']['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, 'libdir') os.mkdir(self.libdir) self.config['directory'] = self.libdir if disk: dbpath = self.config['library'].as_filename() else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir) def teardown_beets(self): self.lib._close() if 'BEETSDIR' in os.environ: del os.environ['BEETSDIR'] self.remove_temp_dir() self.config.clear() beets.config.read(user=False, defaults=True) def load_plugins(self, *plugins): """Load and initialize plugins by names. Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() # Take a backup of the original _types to restore when unloading Item._original_types = dict(Item._types) Album._original_types = dict(Album._types) Item._types.update(beets.plugins.types(Item)) Album._types.update(beets.plugins.types(Album)) def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = [] beets.plugins._classes = set() beets.plugins._instances = {} Item._types = Item._original_types Album._types = Album._original_types 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, 'import') if not os.path.isdir(import_dir): os.mkdir(import_dir) album_no = 0 while album_count: album = 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, 'full.mp3') dest = os.path.join(album_dir, '{0}.mp3'.format(title)) 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]) # Library fixtures methods def create_item(self, **values): """Return an `Item` instance with sensible default values. The item receives its attributes from `**values` paratmeter. The `title`, `artist`, `album`, `track`, `format` and `path` attributes have defaults if they are not given as parameters. The `title` attribute is formated with a running item count to prevent duplicates. The default for the `path` attribute respects the `format` value. The item is attached to the database from `self.lib`. """ item_count = self._get_item_count() values_ = { 'title': u't\u00eftle {0}', 'artist': u'the \u00e4rtist', 'album': u'the \u00e4lbum', 'track': item_count, 'format': 'MP3', } values_.update(values) values_['title'] = values_['title'].format(item_count) values_['db'] = self.lib item = Item(**values_) if 'path' not in values: item['path'] = 'audio.' + item['format'].lower() return item def add_item(self, **values): """Add an item to the library and return it. Creates the item by passing the parameters to `create_item()`. If `path` is not set in `values` it is set to `item.destination()`. """ # When specifying a path, store it normalized (as beets does # ordinarily). if 'path' in values: values['path'] = util.normpath(values['path']) item = self.create_item(**values) item.add(self.lib) # Ensure every item has a path. if 'path' not in values: item['path'] = item.destination() item.store() return item def add_item_fixture(self, **values): """Add an item with an actual audio file to the library. """ item = self.create_item(**values) extension = item['format'].lower() item['path'] = os.path.join(_common.RSRC, 'min.' + extension) item.add(self.lib) item.move(copy=True) item.store() return item def add_album(self, **values): item = self.add_item(**values) return self.lib.add_album([item]) def add_item_fixtures(self, ext='mp3', count=1): """Add a number of items with files to the database. """ # TODO base this on `add_item()` items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(count): item = Item.from_path(bytes(path)) item.album = u'\u00e4lbum {0}'.format(i) # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return items def add_album_fixture(self, track_count=1, ext='mp3'): """Add an album with files to the database. """ items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(track_count): item = Item.from_path(bytes(path)) item.album = u'\u00e4lbum' # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return self.lib.add_album(items) 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 remove_mediafile_fixtures(self): if hasattr(self, '_mediafile_fixtures'): for path in self._mediafile_fixtures: os.remove(path) def _get_item_count(self): if not hasattr(self, '__item_count'): count = 0 self.__item_count = count + 1 return count # Running beets commands def run_command(self, *args): if hasattr(self, 'lib'): lib = self.lib else: lib = Library(':memory:') beets.ui._raw_main(list(args), lib) def run_with_output(self, *args): with capture_stdout() as out: self.run_command(*args) return out.getvalue().decode('utf-8') # Safe file operations def create_temp_dir(self): """Create a temporary directory and assign it into `self.temp_dir`. Call `remove_temp_dir` later to delete it. """ self.temp_dir = mkdtemp() def remove_temp_dir(self): """Delete the temporary directory created by `create_temp_dir`. """ shutil.rmtree(self.temp_dir) def touch(self, path, dir=None, content=''): """Create a file at `path` with given content. If `dir` is given, it is prepended to `path`. After that, if the path is relative, it is resolved with respect to `self.temp_dir`. """ if dir: path = os.path.join(dir, path) if not os.path.isabs(path): path = os.path.join(self.temp_dir, path) parent = os.path.dirname(path) if not os.path.isdir(parent): os.makedirs(parent) with open(path, 'a+') as f: f.write(content) return path
def setUp(self): self.importfeeds = ImportFeedsPlugin() self.lib = Library(':memory:') self.feeds_dir = tempfile.mkdtemp() config['importfeeds']['dir'] = self.feeds_dir
def beets_init(path): return Library(path)
class TestHelper(object): """Helper mixin for high-level cli and plugin tests. This mixin provides methods to isolate beets' global state provide fixtures. """ # TODO automate teardown through hook registration def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = util.py3_path(self.temp_dir) self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = 1 self.config['ui']['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, b'libdir') os.mkdir(self.libdir) self.config['directory'] = util.py3_path(self.libdir) if disk: dbpath = util.bytestring_path(self.config['library'].as_filename()) else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir) def teardown_beets(self): self.lib._close() if 'BEETSDIR' in os.environ: del os.environ['BEETSDIR'] self.remove_temp_dir() self.config.clear() beets.config.read(user=False, defaults=True) def load_plugins(self, *plugins): """Load and initialize plugins by names. Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() # Take a backup of the original _types and _queries to restore # when unloading. Item._original_types = dict(Item._types) Album._original_types = dict(Album._types) Item._types.update(beets.plugins.types(Item)) Album._types.update(beets.plugins.types(Album)) Item._original_queries = dict(Item._queries) Album._original_queries = dict(Album._queries) Item._queries.update(beets.plugins.named_queries(Item)) Album._queries.update(beets.plugins.named_queries(Album)) def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = [] beets.plugins._classes = set() beets.plugins._instances = {} Item._types = Item._original_types Album._types = Album._original_types Item._queries = Item._original_queries Album._queries = Album._original_queries 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]) # Library fixtures methods def create_item(self, **values): """Return an `Item` instance with sensible default values. The item receives its attributes from `**values` paratmeter. The `title`, `artist`, `album`, `track`, `format` and `path` attributes have defaults if they are not given as parameters. The `title` attribute is formated with a running item count to prevent duplicates. The default for the `path` attribute respects the `format` value. The item is attached to the database from `self.lib`. """ item_count = self._get_item_count() values_ = { 'title': u't\u00eftle {0}', 'artist': u'the \u00e4rtist', 'album': u'the \u00e4lbum', 'track': item_count, 'format': 'MP3', } values_.update(values) values_['title'] = values_['title'].format(item_count) values_['db'] = self.lib item = Item(**values_) if 'path' not in values: item['path'] = 'audio.' + item['format'].lower() # mtime needs to be set last since other assignments reset it. item.mtime = 12345 return item def add_item(self, **values): """Add an item to the library and return it. Creates the item by passing the parameters to `create_item()`. If `path` is not set in `values` it is set to `item.destination()`. """ # When specifying a path, store it normalized (as beets does # ordinarily). if 'path' in values: values['path'] = util.normpath(values['path']) item = self.create_item(**values) item.add(self.lib) # Ensure every item has a path. if 'path' not in values: item['path'] = item.destination() item.store() return item def add_item_fixture(self, **values): """Add an item with an actual audio file to the library. """ item = self.create_item(**values) extension = item['format'].lower() item['path'] = os.path.join(_common.RSRC, util.bytestring_path('min.' + extension)) item.add(self.lib) item.move(operation=MoveOperation.COPY) item.store() return item def add_album(self, **values): item = self.add_item(**values) return self.lib.add_album([item]) def add_item_fixtures(self, ext='mp3', count=1): """Add a number of items with files to the database. """ # TODO base this on `add_item()` items = [] path = os.path.join(_common.RSRC, util.bytestring_path('full.' + ext)) for i in range(count): item = Item.from_path(path) item.album = u'\u00e4lbum {0}'.format(i) # Check unicode paths item.title = u't\u00eftle {0}'.format(i) # mtime needs to be set last since other assignments reset it. item.mtime = 12345 item.add(self.lib) item.move(operation=MoveOperation.COPY) item.store() items.append(item) return items def add_album_fixture(self, track_count=1, ext='mp3'): """Add an album with files to the database. """ items = [] path = os.path.join(_common.RSRC, util.bytestring_path('full.' + ext)) for i in range(track_count): item = Item.from_path(path) item.album = u'\u00e4lbum' # Check unicode paths item.title = u't\u00eftle {0}'.format(i) # mtime needs to be set last since other assignments reset it. item.mtime = 12345 item.add(self.lib) item.move(operation=MoveOperation.COPY) item.store() items.append(item) return self.lib.add_album(items) 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 remove_mediafile_fixtures(self): if hasattr(self, '_mediafile_fixtures'): for path in self._mediafile_fixtures: os.remove(path) def _get_item_count(self): if not hasattr(self, '__item_count'): count = 0 self.__item_count = count + 1 return count # Running beets commands def run_command(self, *args, **kwargs): """Run a beets command with an arbitrary amount of arguments. The Library` defaults to `self.lib`, but can be overridden with the keyword argument `lib`. """ sys.argv = ['beet'] # avoid leakage from test suite args lib = None if hasattr(self, 'lib'): lib = self.lib lib = kwargs.get('lib', lib) beets.ui._raw_main(_convert_args(list(args)), lib) def run_with_output(self, *args): with capture_stdout() as out: self.run_command(*args) return util.text_string(out.getvalue()) # Safe file operations def create_temp_dir(self): """Create a temporary directory and assign it into `self.temp_dir`. Call `remove_temp_dir` later to delete it. """ temp_dir = mkdtemp() self.temp_dir = util.bytestring_path(temp_dir) def remove_temp_dir(self): """Delete the temporary directory created by `create_temp_dir`. """ shutil.rmtree(self.temp_dir) def touch(self, path, dir=None, content=''): """Create a file at `path` with given content. If `dir` is given, it is prepended to `path`. After that, if the path is relative, it is resolved with respect to `self.temp_dir`. """ if dir: path = os.path.join(dir, path) if not os.path.isabs(path): path = os.path.join(self.temp_dir, path) parent = os.path.dirname(path) if not os.path.isdir(parent): os.makedirs(util.syspath(parent)) with open(util.syspath(path), 'a+') as f: f.write(content) return path
class TestHelper(object): """Helper mixin for high-level cli and plugin tests. This mixin provides methods to isolate beets' global state provide fixtures. """ # TODO automate teardown through hook registration def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = self.temp_dir self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = True self.config['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, 'libdir') os.mkdir(self.libdir) self.config['directory'] = self.libdir if disk: dbpath = self.config['library'].as_filename() else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir) def teardown_beets(self): del self.lib._connections if 'BEETSDIR' in os.environ: del os.environ['BEETSDIR'] self.remove_temp_dir() self.config.clear() beets.config.read(user=False, defaults=True) def load_plugins(self, *plugins): """Load and initialize plugins by names. Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ beets.config['plugins'] = [] for plugin in beets.plugins._classes: plugin.listeners = None beets.plugins._classes = set() beets.plugins._instances = {} 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, 'import') if not os.path.isdir(import_dir): os.mkdir(import_dir) album_no = 0 while album_count: album = 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 = 'track {0}'.format(track_no) src = os.path.join(_common.RSRC, 'full.mp3') dest = os.path.join(album_dir, '{0}.mp3'.format(title)) 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, logfile=None, query=None, paths=[import_dir]) def add_item_fixtures(self, ext='mp3', count=1): """Add a number of items with files to the database. """ items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(count): item = Item.from_path(str(path)) item.album = u'\u00e4lbum {0}'.format(i) # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return items def add_album_fixture(self, track_count=1, ext='mp3'): """Add an album with files to the database. """ items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(track_count): item = Item.from_path(str(path)) item.album = u'\u00e4lbum' # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return self.lib.add_album(items) def create_mediafile_fixture(self, ext='mp3'): """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()` """ src = os.path.join(_common.RSRC, 'full.' + ext) handle, path = mkstemp() os.close(handle) shutil.copyfile(src, path) if not hasattr(self, '_mediafile_fixtures'): self._mediafile_fixtures = [] self._mediafile_fixtures.append(path) return path def remove_mediafile_fixtures(self): if hasattr(self, '_mediafile_fixtures'): for path in self._mediafile_fixtures: os.remove(path) def run_command(self, *args): if hasattr(self, 'lib'): lib = self.lib else: lib = Library(':memory:') beets.ui._raw_main(list(args), lib) def run_with_output(self, *args): with capture_stdout() as out: self.run_command(*args) return out.getvalue() def create_temp_dir(self): """Create a temporary directory and assign it into `self.temp_dir`. Call `remove_temp_dir` later to delete it. """ self.temp_dir = mkdtemp() def remove_temp_dir(self): """Delete the temporary directory created by `create_temp_dir`. """ shutil.rmtree(self.temp_dir)
class TestHelper(object): """Helper mixin for high-level cli and plugin tests. This mixin provides methods to isolate beets' global state provide fixtures. """ # TODO automate teardown through hook registration def setup_beets(self, disk=False): """Setup pristine global configuration and library for testing. Sets ``beets.config`` so we can safely use any functionality that uses the global configuration. All paths used are contained in a temporary directory Sets the following properties on itself. - ``temp_dir`` Path to a temporary directory containing all files specific to beets - ``libdir`` Path to a subfolder of ``temp_dir``, containing the library's media files. Same as ``config['directory']``. - ``config`` The global configuration used by beets. - ``lib`` Library instance created with the settings from ``config``. Make sure you call ``teardown_beets()`` afterwards. """ self.create_temp_dir() os.environ['BEETSDIR'] = self.temp_dir self.config = beets.config self.config.clear() self.config.read() self.config['plugins'] = [] self.config['verbose'] = True self.config['color'] = False self.config['threaded'] = False self.libdir = os.path.join(self.temp_dir, 'libdir') os.mkdir(self.libdir) self.config['directory'] = self.libdir if disk: dbpath = self.config['library'].as_filename() else: dbpath = ':memory:' self.lib = Library(dbpath, self.libdir) def teardown_beets(self): del self.lib._connections del os.environ['BEETSDIR'] self.remove_temp_dir() self.config.clear() beets.config.read(user=False, defaults=True) def load_plugins(self, *plugins): """Load and initialize plugins by names. Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ beets.config['plugins'] = [] for plugin in beets.plugins._classes: plugin.listeners = None beets.plugins._classes = set() beets.plugins._instances = {} 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 add_item_fixtures(self, ext='mp3', count=1): """Add a number of items with files to the database. """ items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(count): item = Item.from_path(str(path)) item.album = u'\xc3\xa4lbum {0}'.format(i) # Check unicode paths item.title = u't\xc3\x8ftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return items def add_album_fixture(self, track_count=1): """Add an album with files to the database. """ items = [] path = os.path.join(_common.RSRC, 'full.mp3') for i in range(track_count): item = Item.from_path(str(path)) item.album = u'\u00e4lbum' # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) item.move(copy=True) item.store() items.append(item) return self.lib.add_album(items) def create_mediafile_fixture(self, ext='mp3'): """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()` """ src = os.path.join(_common.RSRC, 'full.' + ext) handle, path = mkstemp() os.close(handle) shutil.copyfile(src, path) if not hasattr(self, '_mediafile_fixtures'): self._mediafile_fixtures = [] self._mediafile_fixtures.append(path) return path def remove_mediafile_fixtures(self): if hasattr(self, '_mediafile_fixtures'): for path in self._mediafile_fixtures: os.remove(path) def run_command(self, *args): if hasattr(self, 'lib'): lib = self.lib else: lib = Library(':memory:') beets.ui._raw_main(list(args), lib) def create_temp_dir(self): """Create a temporary directory and assign it into `self.temp_dir`. Call `remove_temp_dir` later to delete it. """ self.temp_dir = mkdtemp() def remove_temp_dir(self): """Delete the temporary directory created by `create_temp_dir`. """ shutil.rmtree(self.temp_dir)