def remove_items(lib, query, album, delete): """Remove items matching query from lib. If album, then match and remove whole albums. If delete, also remove files from disk. """ # Get the matching items. items, albums = _do_query(lib, query, album) # Show all the items. for item in items: ui.print_obj(item, lib) # Confirm with user. print_() if delete: prompt = 'Really DELETE %i files (y/n)?' % len(items) else: prompt = 'Really remove %i items from the library (y/n)?' % \ len(items) if not ui.input_yn(prompt, True): return # Remove (and possibly delete) items. with lib.transaction(): for obj in (albums if album else items): obj.remove(delete)
def remove_items(lib, query, album, delete=False): """Remove items matching query from lib. If album, then match and remove whole albums. If delete, also remove files from disk. """ # Get the matching items. items, albums = _do_query(lib, query, album) # Show all the items. for item in items: print_(item.artist + ' - ' + item.album + ' - ' + item.title) # Confirm with user. print_() if delete: prompt = 'Really DELETE %i files (y/n)?' % len(items) else: prompt = 'Really remove %i items from the library (y/n)?' % \ len(items) if not ui.input_yn(prompt, True): return # Remove (and possibly delete) items. with lib.transaction(): if album: for al in albums: al.remove(delete) else: for item in items: lib.remove(item, delete)
def remove_items(lib, query, album, delete=False): """Remove items matching query from lib. If album, then match and remove whole albums. If delete, also remove files from disk. """ # Get the matching items. items, albums = _do_query(lib, query, album) # Show all the items. for item in items: print_(item.artist + ' - ' + item.album + ' - ' + item.title) # Confirm with user. print_() if delete: prompt = 'Really DELETE %i files (y/n)?' % len(items) else: prompt = 'Really remove %i items from the library (y/n)?' % \ len(items) if not ui.input_yn(prompt, True): return # Remove (and possibly delete) items. if album: for al in albums: al.remove(delete) else: for item in items: lib.remove(item, delete) lib.save()
def convert_func(self, lib, opts, args): dest = opts.dest or self.config['dest'].get() if not dest: raise ui.UserError(u'no convert destination set') dest = util.bytestring_path(dest) threads = opts.threads or self.config['threads'].get(int) path_formats = ui.get_path_formats(self.config['paths'] or None) fmt = opts.format or self.config['format'].as_str().lower() if opts.pretend is not None: pretend = opts.pretend else: pretend = self.config['pretend'].get(bool) if opts.hardlink is not None: hardlink = opts.hardlink link = False elif opts.link is not None: hardlink = False link = opts.link else: hardlink = self.config['hardlink'].get(bool) link = self.config['link'].get(bool) if opts.album: albums = lib.albums(ui.decargs(args)) items = [i for a in albums for i in a.items()] if not pretend: for a in albums: ui.print_(format(a, u'')) else: items = list(lib.items(ui.decargs(args))) if not pretend: for i in items: ui.print_(format(i, u'')) if not items: self._log.error(u'Empty query result.') return if not (pretend or opts.yes or ui.input_yn(u"Convert? (Y/n)")): return if opts.album and self.config['copy_album_art']: for album in albums: self.copy_album_art(album, dest, path_formats, pretend, link, hardlink) convert = [self.convert_item(dest, opts.keep_new, path_formats, fmt, pretend, link, hardlink) for _ in range(threads)] pipe = util.pipeline.Pipeline([iter(items), convert]) pipe.run_parallel()
def modify_items(lib, mods, query, write, move, album, color, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. allowed_keys = library.ALBUM_KEYS if album else library.ITEM_KEYS_WRITABLE fsets = {} for mod in mods: key, value = mod.split('=', 1) if key not in allowed_keys: raise ui.UserError('"%s" is not a valid field' % key) fsets[key] = value # Get the items to modify. items, albums = _do_query(lib, query, album, False) objs = albums if album else items # Preview change. print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item')) for obj in objs: # Identify the changed object. if album: print_(u'* %s - %s' % (obj.albumartist, obj.album)) else: print_(u'* %s - %s' % (obj.artist, obj.title)) # Show each change. for field, value in fsets.iteritems(): curval = getattr(obj, field) _showdiff(field, curval, value, color) # Confirm. if confirm: extra = ' and write tags' if write else '' if not ui.input_yn('Really modify%s (Y/n)?' % extra): return # Apply changes to database. with lib.transaction(): for obj in objs: for field, value in fsets.iteritems(): setattr(obj, field, value) if move: cur_path = obj.item_dir() if album else obj.path if lib.directory in ancestry(cur_path): # In library? log.debug('moving object %s' % cur_path) if album: obj.move() else: lib.move(obj) # When modifying items, we have to store them to the database. if not album: lib.store(obj) # Apply tags if requested. if write: if album: items = itertools.chain(*(a.items() for a in albums)) for item in items: item.write()
def zero_fields(lib, opts, args): if not decargs(args) and not input_yn( u"Remove fields for all items? (Y/n)", True): return for item in lib.items(decargs(args)): self.process_item(item)
def convert_func(lib, opts, args): dest = opts.dest if opts.dest is not None else \ config['convert']['dest'].get() if not dest: raise ui.UserError('no convert destination set') dest = util.bytestring_path(dest) threads = opts.threads if opts.threads is not None else \ config['convert']['threads'].get(int) keep_new = opts.keep_new if not config['convert']['paths']: path_formats = ui.get_path_formats() else: path_formats = ui.get_path_formats(config['convert']['paths']) ui.commands.list_items(lib, ui.decargs(args), opts.album, None) if not ui.input_yn("Convert? (Y/n)"): return if opts.album: items = (i for a in lib.albums(ui.decargs(args)) for i in a.items()) else: items = iter(lib.items(ui.decargs(args))) convert = [ convert_item(dest, keep_new, path_formats) for i in range(threads) ] pipe = util.pipeline.Pipeline([items, convert]) pipe.run_parallel()
def convert_func(self, lib, opts, args): (dest, threads, path_formats, fmt, pretend, hardlink, link) = self._get_opts_and_config(opts) if opts.album: albums = lib.albums(ui.decargs(args)) items = [i for a in albums for i in a.items()] if not pretend: for a in albums: ui.print_(format(a, '')) else: items = list(lib.items(ui.decargs(args))) if not pretend: for i in items: ui.print_(format(i, '')) if not items: self._log.error('Empty query result.') return if not (pretend or opts.yes or ui.input_yn("Convert? (Y/n)")): return if opts.album and self.config['copy_album_art']: for album in albums: self.copy_album_art(album, dest, path_formats, pretend, link, hardlink) self._parallel_convert(dest, opts.keep_new, path_formats, fmt, pretend, link, hardlink, threads, items)
def convert_func(lib, opts, args): dest = opts.dest if opts.dest is not None else \ config['convert']['dest'].get() if not dest: raise ui.UserError('no convert destination set') dest = util.bytestring_path(dest) threads = opts.threads if opts.threads is not None else \ config['convert']['threads'].get(int) keep_new = opts.keep_new if not config['convert']['paths']: path_formats = ui.get_path_formats() else: path_formats = ui.get_path_formats(config['convert']['paths']) ui.commands.list_items(lib, ui.decargs(args), opts.album, None) if not ui.input_yn("Convert? (Y/n)"): return if opts.album: items = (i for a in lib.albums(ui.decargs(args)) for i in a.items()) else: items = iter(lib.items(ui.decargs(args))) convert = [convert_item(lib, dest, keep_new, path_formats) for i in range(threads)] pipe = util.pipeline.Pipeline([items, convert]) pipe.run_parallel()
def modify_items(lib, mods, query, write, move, album, color, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. allowed_keys = library.ALBUM_KEYS if album else library.ITEM_KEYS_WRITABLE fsets = {} for mod in mods: key, value = mod.split('=', 1) if key not in allowed_keys: raise ui.UserError('"%s" is not a valid field' % key) fsets[key] = value # Get the items to modify. items, albums = _do_query(lib, query, album, False) objs = albums if album else items # Preview change. print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item')) for obj in objs: # Identify the changed object. if album: print_(u'* %s - %s' % (obj.albumartist, obj.album)) else: print_(u'* %s - %s' % (obj.artist, obj.title)) # Show each change. for field, value in fsets.iteritems(): curval = getattr(obj, field) _showdiff(field, curval, value, color) # Confirm. if confirm: extra = ' and write tags' if write else '' if not ui.input_yn('Really modify%s (Y/n)?' % extra): return # Apply changes to database. for obj in objs: for field, value in fsets.iteritems(): setattr(obj, field, value) if move: cur_path = obj.item_dir() if album else obj.path if lib.directory in ancestry(cur_path): # In library? log.debug('moving object %s' % cur_path) if album: obj.move() else: lib.move(obj) # When modifying items, we have to store them to the database. if not album: lib.store(obj) lib.save() # Apply tags if requested. if write: if album: items = itertools.chain(*(a.items() for a in albums)) for item in items: item.write()
def modify_items(lib, mods, query, write, move, album, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. model_cls = library.Album if album else library.Item fsets = {} for mod in mods: key, value = mod.split('=', 1) fsets[key] = model_cls._parse(key, value) # Get the items to modify. items, albums = _do_query(lib, query, album, False) objs = albums if album else items # Preview change and collect modified objects. print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item')) changed = set() for obj in objs: # Identify the changed object. ui.print_obj(obj, lib) # Show each change. for field, value in fsets.iteritems(): if _showdiff(field, obj._get_formatted(field), obj._format(field, value)): changed.add(obj) # Still something to do? if not changed: print_('No changes to make.') return # Confirm action. if confirm: extra = ' and write tags' if write else '' if not ui.input_yn('Really modify%s (Y/n)?' % extra): return # Apply changes to database. with lib.transaction(): for obj in changed: for field, value in fsets.iteritems(): obj[field] = value if move: cur_path = obj.path if lib.directory in ancestry(cur_path): # In library? log.debug('moving object %s' % cur_path) obj.move() obj.store() # Apply tags if requested. if write: if album: changed_items = itertools.chain(*(a.items() for a in changed)) else: changed_items = changed for item in changed_items: item.write()
def modify_items(lib, mods, dels, query, write, move, album, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. model_cls = library.Album if album else library.Item fsets = {} for mod in mods: key, value = mod.split('=', 1) fsets[key] = model_cls._parse(key, value) # Get the items to modify. items, albums = _do_query(lib, query, album, False) objs = albums if album else items # Apply changes *temporarily*, preview them, and collect modified # objects. print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item')) changed = set() for obj in objs: for field, value in fsets.iteritems(): obj[field] = value for field in dels: del obj[field] if ui.show_model_changes(obj): changed.add(obj) # Still something to do? if not changed: print_('No changes to make.') return # Confirm action. if confirm: extra = ' and write tags' if write else '' if not ui.input_yn('Really modify%s (Y/n)?' % extra): return # Apply changes to database. with lib.transaction(): for obj in changed: if move: cur_path = obj.path if lib.directory in ancestry(cur_path): # In library? log.debug('moving object %s' % cur_path) obj.move() obj.store() # Apply tags if requested. if write: if album: changed_items = itertools.chain(*(a.items() for a in changed)) else: changed_items = changed for item in changed_items: try: item.write() except library.FileOperationError as exc: log.error(exc)
def read_albums(paths, resume): """A generator yielding all the albums (as sets of Items) found in the user-specified list of paths. `progress` specifies whether the resuming feature should be used. It may be True (resume if possible), False (never resume), or None (ask). """ # Use absolute paths. paths = [library._normpath(path) for path in paths] # Check the user-specified directories. for path in paths: if not os.path.isdir(library._syspath(path)): raise ui.UserError('not a directory: ' + path) # Look for saved progress. progress = resume is not False if progress: resume_dirs = {} for path in paths: resume_dir = progress_get(path) if resume_dir: # Either accept immediately or prompt for input to decide. if resume: do_resume = True ui.print_('Resuming interrupted import of %s' % path) else: do_resume = ui.input_yn("Import of the directory:\n%s" "\nwas interrupted. Resume (Y/n)?" % path) ui.print_() if do_resume: resume_dirs[path] = resume_dir else: # Clear progress; we're starting from the top. progress_set(path, None) for toppath in paths: # Produce each path. if progress: resume_dir = resume_dirs.get(toppath) for path, items in autotag.albums_in_dir(os.path.expanduser(toppath)): if progress and resume_dir: # We're fast-forwarding to resume a previous tagging. if path == resume_dir: # We've hit the last good path! Turn off the # fast-forwarding. resume_dir = None continue yield toppath, path, items # Indicate the directory is finished. yield toppath, DONE_SENTINEL, None
def ask_create(self, create=None): if not self.removable: return True if create is not None: return create msg = u"Collection at '{0}' does not exists. " \ "Maybe you forgot to mount it.\n" \ "Do you want to create the collection? (y/n)" \ .format(displayable_path(self.directory)) return input_yn(msg, require=True)
def modify_items(lib, mods, query, write, move, album, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. fsets = {} for mod in mods: key, value = mod.split('=', 1) fsets[key] = _convert_type(key, value, album) # Get the items to modify. items, albums = _do_query(lib, query, album, False) objs = albums if album else items # Preview change. print_('Modifying %i %ss.' % (len(objs), 'album' if album else 'item')) for obj in objs: # Identify the changed object. ui.print_obj(obj, lib) # Show each change. for field, value in fsets.iteritems(): _showdiff(field, obj.get(field), value) # Confirm. if confirm: extra = ' and write tags' if write else '' if not ui.input_yn('Really modify%s (Y/n)?' % extra): return # Apply changes to database. with lib.transaction(): for obj in objs: for field, value in fsets.iteritems(): obj[field] = value if move: cur_path = obj.item_dir() if album else obj.path if lib.directory in ancestry(cur_path): # In library? log.debug('moving object %s' % cur_path) if album: obj.move() else: lib.move(obj) obj.store() # Apply tags if requested. if write: if album: items = itertools.chain(*(a.items() for a in albums)) for item in items: item.write()
def update(self): if not self.query and not self.force_update: if not input_yn('Do you want to overwrite all ' 'checksums in your database? (y/n)', require=True): return items = self.lib.items(self.query) total = len(items) def update(item): log.debug('updating checksum: {}'.format(item.path)) set_checksum(item) self.execute_with_progress(update, items, msg='Updating checksums')
def convert_func(self, lib, opts, args): dest = opts.dest or self.config['dest'].get() if not dest: raise ui.UserError(u'no convert destination set') dest = util.bytestring_path(dest) threads = opts.threads or self.config['threads'].get(int) path_formats = ui.get_path_formats(self.config['paths'] or None) fmt = opts.format or self.config['format'].as_str().lower() if opts.pretend is not None: pretend = opts.pretend else: pretend = self.config['pretend'].get(bool) if opts.album: albums = lib.albums(ui.decargs(args)) items = [i for a in albums for i in a.items()] if not pretend: for a in albums: ui.print_(format(a, u'')) else: items = list(lib.items(ui.decargs(args))) if not pretend: for i in items: ui.print_(format(i, u'')) if not items: self._log.error(u'Empty query result.') return if not (pretend or opts.yes or ui.input_yn(u"Convert? (Y/n)")): return if opts.album and self.config['copy_album_art']: for album in albums: self.copy_album_art(album, dest, path_formats, pretend) convert = [self.convert_item(dest, opts.keep_new, path_formats, fmt, pretend) for _ in range(threads)] pipe = util.pipeline.Pipeline([iter(items), convert]) pipe.run_parallel()
def update(self): if not self.query and not self.force_update: if not input_yn(u'Do you want to overwrite all ' 'checksums in your database? (y/n)', require=True): return items = self.lib.items(self.query) def update(item): log.debug(u'updating checksum: {}' .format(displayable_path(item.path))) try: set_checksum(item) except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc)) self.execute_with_progress(update, items, msg=u'Updating checksums')
def _confirm(objs, album): """Show the list of affected objects (items or albums) and confirm that the user wants to modify their artwork. `album` is a Boolean indicating whether these are albums (as opposed to items). """ noun = u'album' if album else u'file' prompt = u'Modify artwork for {} {}{} (Y/n)?'.format( len(objs), noun, u's' if len(objs) > 1 else u'') # Show all the items or albums. for obj in objs: print_(format(obj)) # Confirm with user. return ui.input_yn(prompt)
def convert_func(lib, config, opts, args): dest = opts.dest if opts.dest is not None else conf['dest'] if not dest: raise ui.UserError('no convert destination set') threads = opts.threads if opts.threads is not None else conf['threads'] ui.commands.list_items(lib, ui.decargs(args), opts.album, None, config) if not ui.input_yn("Convert? (Y/n)"): return if opts.album: items = (i for a in lib.albums(ui.decargs(args)) for i in a.items()) else: items = lib.items(ui.decargs(args)) convert = [convert_item(lib, dest) for i in range(threads)] pipe = util.pipeline.Pipeline([items, convert]) pipe.run_parallel()
def verify_import_integrity(self, session, task): integrity_errors = [] if not task.items: return for item in task.items: try: verify_integrity(item) except IntegrityError as ex: integrity_errors.append(ex) if integrity_errors: log.warn(u'Warning: failed to verify integrity') for error in integrity_errors: log.warn(u' {}: {}'.format(displayable_path(item.path), error)) if beets.config['import']['quiet'] \ or input_yn(u'Do you want to skip this album (Y/n)'): log.info(u'Skipping.') task.choice_flag = importer.action.SKIP
def import_task_choice_listener(self, session, task): if task.apply: if config['import']['quiet'].get(): return changes = self.show_changes(session.lib, task) # Only prompt when timid if 'timid' in self.config: timid = self.config['timid'].get(bool) else: timid = config['import']['timid'].get() if not changes or not timid: return if not ui.input_yn(u'Continue to apply these changes (Y/n)?'): task.set_choice(importer.action.SKIP)
def convert_func(self, lib, opts, args): if not opts.dest: opts.dest = self.config['dest'].get() if not opts.dest: raise ui.UserError('no convert destination set') opts.dest = util.bytestring_path(opts.dest) if not opts.threads: opts.threads = self.config['threads'].get(int) if self.config['paths']: path_formats = ui.get_path_formats(self.config['paths']) else: path_formats = ui.get_path_formats() if not opts.format: opts.format = self.config['format'].get(unicode).lower() pretend = opts.pretend if opts.pretend is not None else \ self.config['pretend'].get(bool) if not pretend: ui.commands.list_items(lib, ui.decargs(args), opts.album) if not (opts.yes or ui.input_yn("Convert? (Y/n)")): return if opts.album: albums = lib.albums(ui.decargs(args)) items = (i for a in albums for i in a.items()) if self.config['copy_album_art']: for album in albums: self.copy_album_art(album, opts.dest, path_formats, pretend) else: items = iter(lib.items(ui.decargs(args))) convert = [self.convert_item(opts.dest, opts.keep_new, path_formats, opts.format, pretend) for _ in range(opts.threads)] pipe = util.pipeline.Pipeline([items, convert]) pipe.run_parallel()
def fix(self, ask=True): items = list(self.lib.items(self.query)) failed = [] def check(item): try: if 'checksum' in item: verify_checksum(item) fixer = IntegrityChecker.fixer(item) if fixer: fixer.check(item) log.debug(u'{}: {}'.format(colorize('green', u'OK'), displayable_path(item.path))) except IntegrityError: failed.append(item) except ChecksumError: log.error(u'{}: {}'.format(colorize('red', u'FAILED checksum'), displayable_path(item.path))) except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc)) self.execute_with_progress(check, items, msg=u'Verifying integrity') if not failed: self.log(u'No MP3 files to fix') return for item in failed: log.info(displayable_path(item.path)) if ask and not input_yn(u'Do you want to fix these files? {} (y/n)', require=True): return def fix(item): fixer = IntegrityChecker.fixer(item) if fixer: fixer.fix(item) log.debug(u'{}: {}'.format(colorize('green', u'FIXED'), displayable_path(item.path))) set_checksum(item) self.execute_with_progress(fix, failed, msg=u'Fixing files')
def read_albums(paths): """A generator yielding all the albums (as sets of Items) found in the user-specified list of paths. """ # Use absolute paths. paths = [library._normpath(path) for path in paths] # Check the user-specified directories. for path in paths: if not os.path.isdir(library._syspath(path)): raise ui.UserError('not a directory: ' + path) # Look for saved progress. resume_dirs = {} for path in paths: resume_dir = progress_get(path) if resume_dir: resume = ui.input_yn("Import of the directory:\n%s" "\nwas interrupted. Resume (Y/n)? " % path) if resume: resume_dirs[path] = resume_dir else: # Clear progress; we're starting from the top. progress_set(path, None) ui.print_() for toppath in paths: # Produce each path. resume_dir = resume_dirs.get(toppath) for path, items in autotag.albums_in_dir(os.path.expanduser(toppath)): if resume_dir: # We're fast-forwarding to resume a previous tagging. if path == resume_dir: # We've hit the last good path! Turn off the # fast-forwarding. resume_dir = None continue yield toppath, path, items # Indicate the directory is finished. yield toppath, DONE_SENTINEL, None
def _confirm(objs, album): """Show the list of affected objects (items or albums) and confirm that the user wants to modify their artwork. `album` is a Boolean indicating whether these are albums (as opposed to items). """ noun = u'album' if album else u'file' prompt = u'Modify artwork for {} {}{} (Y/n)?'.format( len(objs), noun, u's' if len(objs) > 1 else u'' ) # Show all the items or albums. for obj in objs: print_(format(obj)) # Confirm with user. return ui.input_yn(prompt)
def remove_items(lib, query, album, delete=False): """Remove items matching query from lib. If album, then match and remove whole albums. If delete, also remove files from disk. """ # Get the matching items. if album: albums = list(lib.albums(query=query)) items = [] for al in albums: items += al.items() else: items = list(lib.items(query=query)) if not items: print_('No matching items found.') return # Show all the items. for item in items: print_(item.artist + ' - ' + item.album + ' - ' + item.title) # Confirm with user. print_() if delete: prompt = 'Really DELETE %i files (y/n)?' % len(items) else: prompt = 'Really remove %i items from the library (y/n)?' % \ len(items) if not ui.input_yn(prompt, True): return # Remove (and possibly delete) items. if album: for al in albums: al.remove(delete) else: for item in items: lib.remove(item, delete) lib.save()
def convert_func(lib, opts, args): if not opts.dest: opts.dest = config['convert']['dest'].get() if not opts.dest: raise ui.UserError('no convert destination set') opts.dest = util.bytestring_path(opts.dest) if not opts.threads: opts.threads = config['convert']['threads'].get(int) if config['convert']['paths']: path_formats = ui.get_path_formats(config['convert']['paths']) else: path_formats = ui.get_path_formats() if not opts.format: opts.format = config['convert']['format'].get(unicode).lower() command, ext = get_format(opts.format) ui.commands.list_items(lib, ui.decargs(args), opts.album, None) if not ui.input_yn("Convert? (Y/n)"): return if opts.album: items = (i for a in lib.albums(ui.decargs(args)) for i in a.items()) else: items = iter(lib.items(ui.decargs(args))) convert_stages = [] for i in range(opts.threads): convert_stages.append( convert_item(opts.dest, opts.keep_new, path_formats, command, ext) ) pipe = util.pipeline.Pipeline([items, convert_stages]) pipe.run_parallel()
def edit_objects(self, objs, fields): """Dump a set of Model objects to a file as text, ask the user to edit it, and apply any changes to the objects. Return a boolean indicating whether the edit succeeded. """ # Get the content to edit as raw data structures. old_data = [flatten(o, fields) for o in objs] # Set up a temporary file with the initial data for editing. new = NamedTemporaryFile(suffix='.yaml', delete=False) old_str = dump(old_data) new.write(old_str) new.close() # Loop until we have parseable data and the user confirms. try: while True: # Ask the user to edit the data. edit(new.name) # Read the data back after editing and check whether anything # changed. with open(new.name) as f: new_str = f.read() if new_str == old_str: ui.print_("No changes; aborting.") return False # Parse the updated data. try: new_data = load(new_str) except ParseError as e: ui.print_("Could not read data: {}".format(e)) if ui.input_yn("Edit again to fix? (Y/n)", True): continue else: return False # Show the changes. self.apply_data(objs, old_data, new_data) changed = False for obj in objs: changed |= ui.show_model_changes(obj) if not changed: ui.print_('No changes to apply.') return False # Confirm the changes. choice = ui.input_options( ('continue Editing', 'apply', 'cancel')) if choice == 'a': # Apply. return True elif choice == 'c': # Cancel. return False elif choice == 'e': # Keep editing. # Reset the temporary changes to the objects. for obj in objs: obj.read() continue # Remove the temporary file before returning. finally: os.remove(new.name)
def should_resume(self, path): return ui.input_yn(u"Import of the directory:\n{0}\n" "was interrupted. Resume (Y/n)?".format( displayable_path(path)))
def should_resume(config, path): return ui.input_yn("Import of the directory:\n%s" "\nwas interrupted. Resume (Y/n)?" % path)
def suggest_removal(self, item): """Prompts the user to delete the original path the item was imported from.""" if not item[ 'source_path'] or item.mb_albumid in self.stop_suggestions_for_albums: return if os.path.isdir(item['source_path']): # We ask the user whether they'd like to delete the item's source directory delete = input_yn( "The item:\n{path}\nis originated in the directory:\n{source}\n" "Would you like to delete the source directory of this item?". format(path=colorize_text("text_warning", item.path.decode('utf-8')), source=colorize_text( "text_warning", item['source_path'].decode('utf-8'))), require=True) if delete: self._log.info( "Deleting the item's source which is a directory: %s", item['source_path']) rmtree(item['source_path']) elif os.path.isfile(item['source_path']): # We ask the user whether they'd like to delete the item's source directory print("The item:\n{path}\nis originated in from:\n{source}\n" "What would you like to do?".format( path=colorize_text("text_warning", item.path.decode('utf-8')), source=colorize_text( "text_warning", item['source_path'].decode('utf-8')))) resp = input_options( [ "Delete the item's source", "Recursively delete the source's directory", "do Nothing", "do nothing and Stop suggesting to delete items from this album" ], require=True, ) if resp == 'd': self._log.info("Deleting the item's source file: %s", item['source_path']) os.remove(item['source_path']) elif resp == 'r': source_dir = os.path.dirname(item['source_path']) self._log.info( "Searching for other items with a source_path attr containing: %s", source_dir) # NOTE: I'm not sure why, but we need to escape it twice otherwise the search fails escaped_source_dir = re.escape( re.escape(source_dir.decode("utf-8"))) source_dir_query = "source_path::^{}/*".format( escaped_source_dir) print( "Doing so will delete the following items' sources as well:" ) for searched_item in item._db.items(source_dir_query): print( colorize_text("text_warning", searched_item['path'].decode('utf-8'))) print("Would you like to continue?") continue_resp = input_options( ["Yes", "delete None", "delete just the File"], require=False, # Yes is the a default ) if continue_resp == 'y': self._log.info("Deleting the item's source directory: %s", source_dir) rmtree(source_dir) elif continue_resp == 'n': self._log.info("doing nothing - aborting hook function") return elif continue_resp == 'f': self._log.info( "removing just the item's original source: %s", item['source_path']) os.remove(item['source_path']) elif resp == 's': self.stop_suggestions_for_albums.append(item.mb_albumid) else: self._log.info("Doing nothing")
def edit_objects(self, objs, fields): """Dump a set of Model objects to a file as text, ask the user to edit it, and apply any changes to the objects. Return a boolean indicating whether the edit succeeded. """ # Get the content to edit as raw data structures. old_data = [flatten(o, fields) for o in objs] # Set up a temporary file with the initial data for editing. new = NamedTemporaryFile(suffix=".yaml", delete=False) old_str = dump(old_data) new.write(old_str) new.close() # Loop until we have parseable data and the user confirms. try: while True: # Ask the user to edit the data. edit(new.name) # Read the data back after editing and check whether anything # changed. with open(new.name) as f: new_str = f.read() if new_str == old_str: ui.print_("No changes; aborting.") return False # Parse the updated data. try: new_data = load(new_str) except ParseError as e: ui.print_("Could not read data: {}".format(e)) if ui.input_yn("Edit again to fix? (Y/n)", True): continue else: return False # Show the changes. self.apply_data(objs, old_data, new_data) changed = False for obj in objs: changed |= ui.show_model_changes(obj) if not changed: ui.print_("No changes to apply.") return False # Confirm the changes. choice = ui.input_options(("continue Editing", "apply", "cancel")) if choice == "a": # Apply. return True elif choice == "c": # Cancel. return False elif choice == "e": # Keep editing. # Reset the temporary changes to the objects. for obj in objs: obj.read() continue # Remove the temporary file before returning. finally: os.remove(new.name)
def edit_objects(self, objs, fields): """Dump a set of Model objects to a file as text, ask the user to edit it, and apply any changes to the objects. Return a boolean indicating whether the edit succeeded. """ # Get the content to edit as raw data structures. old_data = [flatten(o, fields) for o in objs] # Set up a temporary file with the initial data for editing. if six.PY2: new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) else: new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False, encoding='utf-8') old_str = dump(old_data) new.write(old_str) if six.PY2: old_str = old_str.decode('utf-8') new.close() # Loop until we have parseable data and the user confirms. try: while True: # Ask the user to edit the data. edit(new.name, self._log) # Read the data back after editing and check whether anything # changed. with codecs.open(new.name, encoding='utf-8') as f: new_str = f.read() if new_str == old_str: ui.print_(u"No changes; aborting.") return False # Parse the updated data. try: new_data = load(new_str) except ParseError as e: ui.print_(u"Could not read data: {}".format(e)) if ui.input_yn(u"Edit again to fix? (Y/n)", True): continue else: return False # Show the changes. # If the objects are not on the DB yet, we need a copy of their # original state for show_model_changes. objs_old = [obj.copy() if obj.id < 0 else None for obj in objs] self.apply_data(objs, old_data, new_data) changed = False for obj, obj_old in zip(objs, objs_old): changed |= ui.show_model_changes(obj, obj_old) if not changed: ui.print_(u'No changes to apply.') return False # Confirm the changes. choice = ui.input_options( (u'continue Editing', u'apply', u'cancel') ) if choice == u'a': # Apply. return True elif choice == u'c': # Cancel. return False elif choice == u'e': # Keep editing. # Reset the temporary changes to the objects. I we have a # copy from above, use that, else reload from the database. objs = [(old_obj or obj) for old_obj, obj in zip(objs_old, objs)] for obj in objs: if not obj.id < 0: obj.load() continue # Remove the temporary file before returning. finally: os.remove(new.name)
def edit_objects(self, objs, fields): """Dump a set of Model objects to a file as text, ask the user to edit it, and apply any changes to the objects. Return a boolean indicating whether the edit succeeded. """ # Get the content to edit as raw data structures. old_data = [flatten(o, fields) for o in objs] # Set up a temporary file with the initial data for editing. if six.PY2: new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) else: new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False, encoding='utf-8') old_str = dump(old_data) new.write(old_str) if six.PY2: old_str = old_str.decode('utf-8') new.close() # Loop until we have parseable data and the user confirms. try: while True: # Ask the user to edit the data. edit(new.name, self._log) # Read the data back after editing and check whether anything # changed. with codecs.open(new.name, encoding='utf-8') as f: new_str = f.read() if new_str == old_str: ui.print_(u"No changes; aborting.") return False # Parse the updated data. try: new_data = load(new_str) except ParseError as e: ui.print_(u"Could not read data: {}".format(e)) if ui.input_yn(u"Edit again to fix? (Y/n)", True): continue else: return False # Show the changes. # If the objects are not on the DB yet, we need a copy of their # original state for show_model_changes. objs_old = [ deepcopy(obj) if not obj._db else None for obj in objs ] self.apply_data(objs, old_data, new_data) changed = False for obj, obj_old in zip(objs, objs_old): changed |= ui.show_model_changes(obj, obj_old) if not changed: ui.print_(u'No changes to apply.') return False # Confirm the changes. choice = ui.input_options( (u'continue Editing', u'apply', u'cancel')) if choice == u'a': # Apply. return True elif choice == u'c': # Cancel. return False elif choice == u'e': # Keep editing. # Reset the temporary changes to the objects. for obj in objs: obj.read() continue # Remove the temporary file before returning. finally: os.remove(new.name)
def should_resume(self, path): return ui.input_yn(u"Import of the directory:\n{0}\n" "was interrupted. Resume (Y/n)?" .format(displayable_path(path)))