def item_file(item_id): item = g.lib.get_item(item_id) # On Windows under Python 2, Flask wants a Unicode path. On Python 3, it # *always* wants a Unicode path. if os.name == 'nt': item_path = util.syspath(item.path) else: item_path = util.py3_path(item.path) try: unicode_item_path = util.text_string(item.path) except (UnicodeDecodeError, UnicodeEncodeError): unicode_item_path = util.displayable_path(item.path) base_filename = os.path.basename(unicode_item_path) try: # Imitate http.server behaviour base_filename.encode("latin-1", "strict") except UnicodeEncodeError: safe_filename = unidecode(base_filename) else: safe_filename = base_filename response = flask.send_file(item_path, as_attachment=True, attachment_filename=safe_filename) response.headers['Content-Length'] = os.path.getsize(item_path) return response
def item_file(item_id): item = g.lib.get_item(item_id) # On Windows under Python 2, Flask wants a Unicode path. On Python 3, it # *always* wants a Unicode path. if os.name == 'nt': item_path = util.syspath(item.path) else: item_path = util.py3_path(item.path) try: unicode_item_path = util.text_string(item.path) except (UnicodeDecodeError, UnicodeEncodeError): unicode_item_path = util.displayable_path(item.path) base_filename = os.path.basename(unicode_item_path) try: # Imitate http.server behaviour base_filename.encode("latin-1", "strict") except UnicodeEncodeError: safe_filename = unidecode(base_filename) else: safe_filename = base_filename response = flask.send_file( item_path, as_attachment=True, attachment_filename=safe_filename ) response.headers['Content-Length'] = os.path.getsize(item_path) return response
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) bin = self.config['bin'].as_str() for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output( [bin, '-f', util.syspath(item.path)]) except (subprocess.CalledProcessError, OSError) as exc: self._log.error(u'execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. # http://bugs.python.org/issue1759845 self._log.error(u'execution failed for Unicode path: {0!r}', item.path) continue key_raw = output.rsplit(None, 1)[-1] try: key = util.text_string(key_raw) except UnicodeDecodeError: self._log.error(u'output is invalid UTF-8') continue item['initial_key'] = key self._log.info(u'added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def _store_dict(option, opt_str, value, parser): """Custom action callback to parse options which have ``key=value`` pairs as values. All such pairs passed for this option are aggregated into a dictionary. """ dest = option.dest option_values = getattr(parser.values, dest, None) if option_values is None: # This is the first supplied ``key=value`` pair of option. # Initialize empty dictionary and get a reference to it. setattr(parser.values, dest, {}) option_values = getattr(parser.values, dest) # Decode the argument using the platform's argument encoding. value = util.text_string(value, util.arg_encoding()) try: key, value = value.split('=', 1) if not (key and value): raise ValueError except ValueError: raise UserError( "supplied argument `{}' is not of the form `key=value'".format( value)) option_values[key] = value
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) bin = self.config['bin'].as_str() for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output([bin, '-f', util.syspath(item.path)]) except (subprocess.CalledProcessError, OSError) as exc: self._log.error(u'execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. # http://bugs.python.org/issue1759845 self._log.error(u'execution failed for Unicode path: {0!r}', item.path) continue key_raw = output.rsplit(None, 1)[-1] try: key = util.text_string(key_raw) except UnicodeDecodeError: self._log.error(u'output is invalid UTF-8') continue item['initial_key'] = key self._log.info(u'added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def _show_change(self, items=None, info=None, cur_artist=u'the artist', cur_album=u'the album', dist=0.1): """Return an unicode string representing the changes""" items = items or self.items info = info or self.info mapping = dict(zip(items, info.tracks)) config['ui']['color'] = False album_dist = distance(items, info, mapping) album_dist._penalties = {'album': [dist]} commands.show_change( cur_artist, cur_album, autotag.AlbumMatch(album_dist, info, mapping, set(), set()), ) # FIXME decoding shouldn't be done here return util.text_string(self.io.getoutput().lower())
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) command = [self.config['bin'].as_str()] # The KeyFinder GUI program needs the -f flag before the path. # keyfinder-cli is similar, but just wants the path with no flag. if 'keyfinder-cli' not in os.path.basename(command[0]).lower(): command.append('-f') for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output(command + [util.syspath(item.path)]).stdout except (subprocess.CalledProcessError, OSError) as exc: self._log.error('execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. # https://bugs.python.org/issue1759845 self._log.error('execution failed for Unicode path: {0!r}', item.path) continue try: key_raw = output.rsplit(None, 1)[-1] except IndexError: # Sometimes keyfinder-cli returns 0 but with no key, usually # when the file is silent or corrupt, so we log and skip. self._log.error('no key returned for path: {0}', item.path) continue try: key = util.text_string(key_raw) except UnicodeDecodeError: self._log.error('output is invalid UTF-8') continue item['initial_key'] = key self._log.info('added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def _show_change(self, items=None, info=None, cur_artist=u'the artist', cur_album=u'the album', dist=0.1): """Return an unicode string representing the changes""" items = items or self.items info = info or self.info mapping = dict(zip(items, info.tracks)) config['ui']['color'] = False album_dist = distance(items, info, mapping) album_dist._penalties = {'album': [dist]} commands.show_change( cur_artist, cur_album, autotag.AlbumMatch(album_dist, info, mapping, set(), set()), ) # FIXME decoding shouldn't be done here return util.text_string(self.io.getoutput().lower())
def _store_dict(option, opt_str, value, parser): """Custom action callback to parse options which have ``key=value`` pairs as values. All such pairs passed for this option are aggregated into a dictionary. """ dest = option.dest option_values = getattr(parser.values, dest, None) if option_values is None: # This is the first supplied ``key=value`` pair of option. # Initialize empty dictionary and get a reference to it. setattr(parser.values, dest, dict()) option_values = getattr(parser.values, dest) try: key, value = map(lambda s: util.text_string(s), value.split('=')) if not (key and value): raise ValueError except ValueError: raise UserError( "supplied argument `{0}' is not of the form `key=value'" .format(value)) option_values[key] = value
def run_with_output(self, *args): with capture_stdout() as out: self.run_command(*args) return util.text_string(out.getvalue())
def run_with_log_capture(self, *args): with capture_log() as out: self.run_command(*args) return util.text_string("\n".join(out))
def run_with_output(self, *args): with capture_stdout() as out: self.run_command(*args) return util.text_string(out.getvalue())
def play_music(self, lib, opts, args): """Execute query, create temporary playlist and execute player command passing that playlist, at request insert optional arguments. """ command_str = config['play']['command'].get() if not command_str: command_str = util.open_anything() use_folders = config['play']['use_folders'].get(bool) relative_to = config['play']['relative_to'].get() raw = config['play']['raw'].get(bool) warning_threshold = config['play']['warning_threshold'].get(int) # We use -2 as a default value for warning_threshold to detect if it is # set or not. We can't use a falsey value because it would have an # actual meaning in the configuration of this plugin, and we do not use # -1 because some people might use it as a value to obtain no warning, # which wouldn't be that bad of a practice. if warning_threshold == -2: # if warning_threshold has not been set by user, look for # warning_treshold, to preserve backwards compatibility. See #1803. # warning_treshold has the correct default value of 100. warning_threshold = config['play']['warning_treshold'].get(int) if relative_to: relative_to = util.normpath(relative_to) # Add optional arguments to the player command. if opts.args: if ARGS_MARKER in command_str: command_str = command_str.replace(ARGS_MARKER, opts.args) else: command_str = u"{} {}".format(command_str, opts.args) # Perform search by album and add folders rather than tracks to # playlist. if opts.album: selection = lib.albums(ui.decargs(args)) paths = [] sort = lib.get_default_album_sort() for album in selection: if use_folders: paths.append(album.item_dir()) else: paths.extend(item.path for item in sort.sort(album.items())) item_type = 'album' # Perform item query and add tracks to playlist. else: selection = lib.items(ui.decargs(args)) paths = [item.path for item in selection] if relative_to: paths = [relpath(path, relative_to) for path in paths] item_type = 'track' item_type += 's' if len(selection) > 1 else '' if not selection: ui.print_(ui.colorize('text_warning', u'No {0} to play.'.format(item_type))) return # Warn user before playing any huge playlists. if warning_threshold and len(selection) > warning_threshold: ui.print_(ui.colorize( 'text_warning', u'You are about to queue {0} {1}.'.format( len(selection), item_type))) if ui.input_options((u'Continue', u'Abort')) == 'a': return ui.print_(u'Playing {0} {1}.'.format(len(selection), item_type)) if raw: open_args = paths else: open_args = [self._create_tmp_playlist(paths)] self._log.debug(u'executing command: {} {}', command_str, util.text_string(b' '.join(open_args))) try: util.interactive_open(open_args, command_str) except OSError as exc: raise ui.UserError( "Could not play the query: {0}".format(exc))