Пример #1
0
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
Пример #2
0
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
Пример #3
0
    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()
Пример #4
0
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
Пример #5
0
    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()
Пример #6
0
 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())
Пример #7
0
    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()
Пример #8
0
 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())
Пример #9
0
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
Пример #10
0
 def run_with_output(self, *args):
     with capture_stdout() as out:
         self.run_command(*args)
     return util.text_string(out.getvalue())
Пример #11
0
 def run_with_log_capture(self, *args):
     with capture_log() as out:
         self.run_command(*args)
     return util.text_string("\n".join(out))
Пример #12
0
 def run_with_output(self, *args):
     with capture_stdout() as out:
         self.run_command(*args)
     return util.text_string(out.getvalue())
Пример #13
0
    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))