Example #1
0
    def setUp(self):
        self.setup_beets()

        # Don't use the BEETSDIR from `helper`. Instead, we point the home
        # directory there. Some tests will set `BEETSDIR` themselves.
        del os.environ['BEETSDIR']
        self._old_home = os.environ.get('HOME')
        os.environ['HOME'] = util.py3_path(self.temp_dir)

        # Also set APPDATA, the Windows equivalent of setting $HOME.
        self._old_appdata = os.environ.get('APPDATA')
        os.environ['APPDATA'] = \
            util.py3_path(os.path.join(self.temp_dir, b'AppData', b'Roaming'))

        self._orig_cwd = os.getcwd()
        self.test_cmd = self._make_test_cmd()
        commands.default_commands.append(self.test_cmd)

        # Default user configuration
        if platform.system() == 'Windows':
            self.user_config_dir = os.path.join(self.temp_dir, b'AppData',
                                                b'Roaming', b'beets')
        else:
            self.user_config_dir = os.path.join(self.temp_dir, b'.config',
                                                b'beets')
        os.makedirs(self.user_config_dir)
        self.user_config_path = os.path.join(self.user_config_dir,
                                             b'config.yaml')

        # Custom BEETSDIR
        self.beetsdir = os.path.join(self.temp_dir, b'beetsdir')
        os.makedirs(self.beetsdir)

        self._reset_config()
Example #2
0
    def convert_on_import(self, lib, item):
        """Transcode a file automatically after it is imported into the
        library.
        """
        fmt = self.config['format'].as_str().lower()
        if should_transcode(item, fmt):
            command, ext = get_format()

            # Create a temporary file for the conversion.
            tmpdir = self.config['tmpdir'].get()
            if tmpdir:
                tmpdir = util.py3_path(util.bytestring_path(tmpdir))
            fd, dest = tempfile.mkstemp(util.py3_path(b'.' + ext), dir=tmpdir)
            os.close(fd)
            dest = util.bytestring_path(dest)
            _temp_files.append(dest)  # Delete the transcode later.

            # Convert.
            try:
                self.encode(command, item.path, dest)
            except subprocess.CalledProcessError:
                return

            # Change the newly-imported database entry to point to the
            # converted file.
            item.path = dest
            item.write()
            item.read()  # Load new audio information data.
            item.store()
Example #3
0
    def convert_on_import(self, lib, item):
        """Transcode a file automatically after it is imported into the
        library.
        """
        fmt = self.config['format'].as_str().lower()
        if should_transcode(item, fmt):
            command, ext = get_format()

            # Create a temporary file for the conversion.
            tmpdir = self.config['tmpdir'].get()
            if tmpdir:
                tmpdir = util.py3_path(util.bytestring_path(tmpdir))
            fd, dest = tempfile.mkstemp(util.py3_path(b'.' + ext), dir=tmpdir)
            os.close(fd)
            dest = util.bytestring_path(dest)
            _temp_files.append(dest)  # Delete the transcode later.

            # Convert.
            try:
                self.encode(command, item.path, dest)
            except subprocess.CalledProcessError:
                return

            # Change the newly-imported database entry to point to the
            # converted file.
            source_path = item.path
            item.path = dest
            item.write()
            item.read()  # Load new audio information data.
            item.store()

            if self.config['delete_originals']:
                self._log.info(u'Removing original file {0}', source_path)
                util.remove(source_path, False)
Example #4
0
    def setUp(self):
        self.setup_beets()

        # Don't use the BEETSDIR from `helper`. Instead, we point the home
        # directory there. Some tests will set `BEETSDIR` themselves.
        del os.environ['BEETSDIR']
        self._old_home = os.environ.get('HOME')
        os.environ['HOME'] = util.py3_path(self.temp_dir)

        # Also set APPDATA, the Windows equivalent of setting $HOME.
        self._old_appdata = os.environ.get('APPDATA')
        os.environ['APPDATA'] = \
            util.py3_path(os.path.join(self.temp_dir, b'AppData', b'Roaming'))

        self._orig_cwd = os.getcwd()
        self.test_cmd = self._make_test_cmd()
        commands.default_commands.append(self.test_cmd)

        # Default user configuration
        if platform.system() == 'Windows':
            self.user_config_dir = os.path.join(self.temp_dir, b'AppData',
                                                b'Roaming', b'beets')
        else:
            self.user_config_dir = os.path.join(self.temp_dir, b'.config',
                                                b'beets')
        os.makedirs(self.user_config_dir)
        self.user_config_path = os.path.join(self.user_config_dir,
                                             b'config.yaml')

        # Custom BEETSDIR
        self.beetsdir = os.path.join(self.temp_dir, b'beetsdir')
        os.makedirs(self.beetsdir)

        self._reset_config()
Example #5
0
    def run_bpd(self, host='localhost', password=None, do_hello=True,
                second_client=False):
        """ Runs BPD in another process, configured with the same library
        database as we created in the setUp method. Exposes a client that is
        connected to the server, and kills the server at the end.
        """
        # Create a config file:
        config = {
                'pluginpath': [py3_path(self.temp_dir)],
                'plugins': 'bpd',
                # use port 0 to let the OS choose a free port
                'bpd': {'host': host, 'port': 0, 'control_port': 0},
        }
        if password:
            config['bpd']['password'] = password
        config_file = tempfile.NamedTemporaryFile(
                mode='wb', dir=py3_path(self.temp_dir), suffix='.yaml',
                delete=False)
        config_file.write(
                yaml.dump(config, Dumper=confuse.Dumper, encoding='utf-8'))
        config_file.close()

        # Fork and launch BPD in the new process:
        assigned_port = mp.Queue(2)  # 2 slots, `control_port` and `port`
        server = mp.Process(target=start_server, args=([
            '--library', self.config['library'].as_filename(),
            '--directory', py3_path(self.libdir),
            '--config', py3_path(config_file.name),
            'bpd'
        ], assigned_port))
        server.start()

        try:
            assigned_port.get(timeout=1)  # skip control_port
            port = assigned_port.get(timeout=0.5)  # read port

            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                sock.connect((host, port))

                if second_client:
                    sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    try:
                        sock2.connect((host, port))
                        yield (
                            MPCClient(sock, do_hello),
                            MPCClient(sock2, do_hello),
                        )
                    finally:
                        sock2.close()

                else:
                    yield MPCClient(sock, do_hello)
            finally:
                sock.close()
        finally:
            server.terminate()
            server.join(timeout=0.2)
Example #6
0
def pil_resize(maxwidth, path_in, path_out=None, quality=0, max_filesize=0):
    """Resize using Python Imaging Library (PIL).  Return the output path
    of resized image.
    """
    path_out = path_out or temp_file_for(path_in)
    from PIL import Image

    log.debug('artresizer: PIL resizing {0} to {1}',
              util.displayable_path(path_in), util.displayable_path(path_out))

    try:
        im = Image.open(util.syspath(path_in))
        size = maxwidth, maxwidth
        im.thumbnail(size, Image.ANTIALIAS)

        if quality == 0:
            # Use PIL's default quality.
            quality = -1

        # progressive=False only affects JPEGs and is the default,
        # but we include it here for explicitness.
        im.save(util.py3_path(path_out), quality=quality, progressive=False)

        if max_filesize > 0:
            # If maximum filesize is set, we attempt to lower the quality of
            # jpeg conversion by a proportional amount, up to 3 attempts
            # First, set the maximum quality to either provided, or 95
            if quality > 0:
                lower_qual = quality
            else:
                lower_qual = 95
            for i in range(5):
                # 5 attempts is an abitrary choice
                filesize = os.stat(util.syspath(path_out)).st_size
                log.debug("PIL Pass {0} : Output size: {1}B", i, filesize)
                if filesize <= max_filesize:
                    return path_out
                # The relationship between filesize & quality will be
                # image dependent.
                lower_qual -= 10
                # Restrict quality dropping below 10
                if lower_qual < 10:
                    lower_qual = 10
                # Use optimize flag to improve filesize decrease
                im.save(util.py3_path(path_out),
                        quality=lower_qual,
                        optimize=True,
                        progressive=False)
            log.warning("PIL Failed to resize file to below {0}B",
                        max_filesize)
            return path_out

        else:
            return path_out
    except OSError:
        log.error("PIL cannot create thumbnail for '{0}'",
                  util.displayable_path(path_in))
        return path_in
Example #7
0
def item_file(item_id):
    item = g.lib.get_item(item_id)
    response = flask.send_file(
        util.py3_path(item.path),
        as_attachment=True,
        attachment_filename=os.path.basename(util.py3_path(item.path)),
    )
    response.headers['Content-Length'] = os.path.getsize(item.path)
    return response
Example #8
0
    def run_bpd(self, host='localhost', port=9876, password=None,
                do_hello=True, second_client=False):
        """ Runs BPD in another process, configured with the same library
        database as we created in the setUp method. Exposes a client that is
        connected to the server, and kills the server at the end.
        """
        # Create a config file:
        config = {
                'pluginpath': [py3_path(self.temp_dir)],
                'plugins': 'bpd',
                'bpd': {'host': host, 'port': port, 'control_port': port + 1},
        }
        if password:
            config['bpd']['password'] = password
        config_file = tempfile.NamedTemporaryFile(
                mode='wb', dir=py3_path(self.temp_dir), suffix='.yaml',
                delete=False)
        config_file.write(
                yaml.dump(config, Dumper=confit.Dumper, encoding='utf-8'))
        config_file.close()

        # Fork and launch BPD in the new process:
        args = (
            '--library', self.config['library'].as_filename(),
            '--directory', py3_path(self.libdir),
            '--config', py3_path(config_file.name),
            'bpd'
        )
        server = mp.Process(target=start_beets, args=args)
        server.start()

        # Wait until the socket is connected:
        sock, sock2 = None, None
        for _ in range(20):
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if sock.connect_ex((host, port)) == 0:
                break
            else:
                sock.close()
                time.sleep(0.01)
        else:
            raise RuntimeError('Timed out waiting for the BPD server')

        try:
            if second_client:
                sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock2.connect((host, port))
                yield MPCClient(sock, do_hello), MPCClient(sock2, do_hello)
            else:
                yield MPCClient(sock, do_hello)
        finally:
            sock.close()
            if sock2:
                sock2.close()
            server.terminate()
            server.join(timeout=0.2)
Example #9
0
    def setUp(self):
        self.setup_beets()
        self.load_plugins('metasync')

        self.config['metasync']['source'] = 'itunes'

        if _is_windows():
            self.config['metasync']['itunes']['library'] = \
                py3_path(self.itunes_library_windows)
        else:
            self.config['metasync']['itunes']['library'] = \
                py3_path(self.itunes_library_unix)

        self._set_up_data()
Example #10
0
    def setUp(self):
        self.setup_beets()
        self.load_plugins('metasync')

        self.config['metasync']['source'] = 'itunes'

        if _is_windows():
            self.config['metasync']['itunes']['library'] = \
                py3_path(self.itunes_library_windows)
        else:
            self.config['metasync']['itunes']['library'] = \
                py3_path(self.itunes_library_unix)

        self._set_up_data()
Example #11
0
    def run_bpd(self, host='localhost', port=9876, password=None,
                do_hello=True):
        """ Runs BPD in another process, configured with the same library
        database as we created in the setUp method. Exposes a client that is
        connected to the server, and kills the server at the end.
        """
        # Create a config file:
        config = {
                'pluginpath': [py3_path(self.temp_dir)],
                'plugins': 'bpd',
                'bpd': {'host': host, 'port': port},
        }
        if password:
            config['bpd']['password'] = password
        config_file = tempfile.NamedTemporaryFile(
                mode='wb', dir=py3_path(self.temp_dir), suffix='.yaml',
                delete=False)
        config_file.write(
                yaml.dump(config, Dumper=confit.Dumper, encoding='utf-8'))
        config_file.close()

        # Fork and launch BPD in the new process:
        args = (
            '--library', self.config['library'].as_filename(),
            '--directory', py3_path(self.libdir),
            '--config', py3_path(config_file.name),
            'bpd'
        )
        server = mp.Process(target=start_beets, args=args)
        server.start()

        # Wait until the socket is connected:
        sock = None
        for _ in range(20):
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if sock.connect_ex((host, port)) == 0:
                break
            else:
                sock.close()
                time.sleep(0.01)
        else:
            raise RuntimeError('Timed out waiting for the BPD server')

        try:
            yield MPCClient(sock, do_hello)
        finally:
            sock.close()
            server.terminate()
            server.join(timeout=0.2)
    def test_playlist_update(self):
        spl = SmartPlaylistPlugin()

        i = Mock(path=b'/tagada.mp3')
        i.evaluate_template.side_effect = lambda x, _: x
        q = Mock()
        a_q = Mock()
        lib = Mock()
        lib.items.return_value = [i]
        lib.albums.return_value = []
        pl = b'my_playlist.m3u', (q, None), (a_q, None)
        spl._matched_playlists = [pl]

        dir = bytestring_path(mkdtemp())
        config['smartplaylist']['relative_to'] = False
        config['smartplaylist']['playlist_dir'] = py3_path(dir)
        try:
            spl.update_playlists(lib)
        except Exception:
            rmtree(dir)
            raise

        lib.items.assert_called_once_with(q, None)
        lib.albums.assert_called_once_with(a_q, None)

        m3u_filepath = path.join(dir, pl[0])
        self.assertTrue(path.exists(m3u_filepath))
        with open(syspath(m3u_filepath), 'rb') as f:
            content = f.read()
        rmtree(dir)

        self.assertEqual(content, b'/tagada.mp3\n')
Example #13
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
Example #14
0
    def _set_file(self):
        """Initialize the filesrc element with the next file to be analyzed.
        """
        # No more files, we're done
        if len(self._files) == 0:
            return False

        self._file = self._files.pop(0)

        # Ensure the filesrc element received the paused state of the
        # pipeline in a blocking manner
        self._src.sync_state_with_parent()
        self._src.get_state(self.Gst.CLOCK_TIME_NONE)

        # Ensure the decodebin element receives the paused state of the
        # pipeline in a blocking manner
        self._decbin.sync_state_with_parent()
        self._decbin.get_state(self.Gst.CLOCK_TIME_NONE)

        # Disconnect the decodebin element from the pipeline, set its
        # state to READY to to clear it.
        self._decbin.unlink(self._conv)
        self._decbin.set_state(self.Gst.State.READY)

        # Set a new file on the filesrc element, can only be done in the
        # READY state
        self._src.set_state(self.Gst.State.READY)
        self._src.set_property("location", py3_path(syspath(self._file.path)))

        self._decbin.link(self._conv)
        self._pipe.set_state(self.Gst.State.READY)

        return True
Example #15
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
Example #16
0
    def _set_file(self):
        """Initialize the filesrc element with the next file to be analyzed.
        """
        # No more files, we're done
        if len(self._files) == 0:
            return False

        self._file = self._files.pop(0)

        # Ensure the filesrc element received the paused state of the
        # pipeline in a blocking manner
        self._src.sync_state_with_parent()
        self._src.get_state(self.Gst.CLOCK_TIME_NONE)

        # Ensure the decodebin element receives the paused state of the
        # pipeline in a blocking manner
        self._decbin.sync_state_with_parent()
        self._decbin.get_state(self.Gst.CLOCK_TIME_NONE)

        # Disconnect the decodebin element from the pipeline, set its
        # state to READY to to clear it.
        self._decbin.unlink(self._conv)
        self._decbin.set_state(self.Gst.State.READY)

        # Set a new file on the filesrc element, can only be done in the
        # READY state
        self._src.set_state(self.Gst.State.READY)
        self._src.set_property("location", py3_path(syspath(self._file.path)))

        self._decbin.link(self._conv)
        self._pipe.set_state(self.Gst.State.READY)

        return True
Example #17
0
def temp_file_for(path):
    """Return an unused filename with the same extension as the
    specified path.
    """
    ext = os.path.splitext(path)[1]
    with NamedTemporaryFile(suffix=util.py3_path(ext), delete=False) as f:
        return util.bytestring_path(f.name)
Example #18
0
def temp_file_for(path):
    """Return an unused filename with the same extension as the
    specified path.
    """
    ext = os.path.splitext(path)[1]
    with NamedTemporaryFile(suffix=util.py3_path(ext), delete=False) as f:
        return util.bytestring_path(f.name)
Example #19
0
    def test_playlist_update(self):
        spl = SmartPlaylistPlugin()

        i = Mock(path=b'/tagada.mp3')
        i.evaluate_template.side_effect = lambda x, _: x
        q = Mock()
        a_q = Mock()
        lib = Mock()
        lib.items.return_value = [i]
        lib.albums.return_value = []
        pl = b'my_playlist.m3u', (q, None), (a_q, None)
        spl._matched_playlists = [pl]

        dir = bytestring_path(mkdtemp())
        config['smartplaylist']['relative_to'] = False
        config['smartplaylist']['playlist_dir'] = py3_path(dir)
        try:
            spl.update_playlists(lib)
        except Exception:
            rmtree(dir)
            raise

        lib.items.assert_called_once_with(q, None)
        lib.albums.assert_called_once_with(a_q, None)

        m3u_filepath = path.join(dir, pl[0])
        self.assertTrue(path.exists(m3u_filepath))
        with open(syspath(m3u_filepath), 'rb') as f:
            content = f.read()
        rmtree(dir)

        self.assertEqual(content, b'/tagada.mp3\n')
Example #20
0
def _load_plugins(options, config):
    """Load the plugins specified on the command line or in the configuration.
    """
    paths = config['pluginpath'].as_str_seq(split=False)
    paths = [util.normpath(p) for p in paths]
    log.debug('plugin paths: {0}', util.displayable_path(paths))

    # On Python 3, the search paths need to be unicode.
    paths = [util.py3_path(p) for p in paths]

    # Extend the `beetsplug` package to include the plugin paths.
    import beetsplug
    beetsplug.__path__ = paths + list(beetsplug.__path__)

    # For backwards compatibility, also support plugin paths that
    # *contain* a `beetsplug` package.
    sys.path += paths

    # If we were given any plugins on the command line, use those.
    if options.plugins is not None:
        plugin_list = (options.plugins.split(',')
                       if len(options.plugins) > 0 else [])
    else:
        plugin_list = config['plugins'].as_str_seq()

    plugins.load_plugins(plugin_list)
    plugins.send("pluginload")
    return plugins
Example #21
0
    def test_beetsdir_config_does_not_load_default_user_config(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        with open(self.user_config_path, 'w') as file:
            file.write('anoption: value')

        config.read()
        self.assertFalse(config['anoption'].exists())
Example #22
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)

    response = flask.send_file(
        item_path,
        as_attachment=True,
        attachment_filename=os.path.basename(util.py3_path(item.path)),
    )
    response.headers['Content-Length'] = os.path.getsize(item_path)
    return response
Example #23
0
    def test_beetsdir_config_does_not_load_default_user_config(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        with open(self.user_config_path, 'w') as file:
            file.write('anoption: value')

        config.read()
        self.assertFalse(config['anoption'].exists())
Example #24
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)

    response = flask.send_file(
        item_path,
        as_attachment=True,
        attachment_filename=os.path.basename(util.py3_path(item.path)),
    )
    response.headers['Content-Length'] = os.path.getsize(item_path)
    return response
Example #25
0
    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)
Example #26
0
    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)
Example #27
0
    def test_beetsdir_config(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('anoption: overwrite')

        config.read()
        self.assertEqual(config['anoption'].get(), 'overwrite')
Example #28
0
    def _set_first_file(self):
        if len(self._files) == 0:
            return False

        self._file = self._files.pop(0)
        self._pipe.set_state(self.Gst.State.NULL)
        self._src.set_property("location", py3_path(syspath(self._file.path)))
        self._pipe.set_state(self.Gst.State.PLAYING)
        return True
Example #29
0
    def _set_first_file(self):
        if len(self._files) == 0:
            return False

        self._file = self._files.pop(0)
        self._pipe.set_state(self.Gst.State.NULL)
        self._src.set_property("location", py3_path(syspath(self._file.path)))
        self._pipe.set_state(self.Gst.State.PLAYING)
        return True
Example #30
0
    def resource_object(album):
        """Construct a JSON:API resource object from a beets Album.

        Args:
            album: A beets Album object.
        """
        attributes = {}
        # Use aura => beets attribute name map
        for aura_attr, beets_attr in ALBUM_ATTR_MAP.items():
            a = getattr(album, beets_attr)
            # Only set attribute if it's not None, 0, "", etc.
            # NOTE: This could mean required attributes are not set
            if a:
                attributes[aura_attr] = a

        # Get beets Item objects for all tracks in the album sorted by
        # track number. Sorting is not required but it's nice.
        query = MatchQuery("album_id", album.id)
        sort = FixedFieldSort("track", ascending=True)
        tracks = current_app.config["lib"].items(query, sort)
        # JSON:API one-to-many relationship to tracks on the album
        relationships = {
            "tracks": {
                "data": [{
                    "type": "track",
                    "id": str(t.id)
                } for t in tracks]
            }
        }
        # Add images relationship if album has associated images
        if album.artpath:
            path = py3_path(album.artpath)
            filename = path.split("/")[-1]
            image_id = f"album-{album.id}-{filename}"
            relationships["images"] = {
                "data": [{
                    "type": "image",
                    "id": image_id
                }]
            }
        # Add artist relationship if artist name is same on tracks
        # Tracks are used to define artists so don't albumartist
        # Check for all tracks in case some have featured artists
        if album.albumartist in [t.artist for t in tracks]:
            relationships["artists"] = {
                "data": [{
                    "type": "artist",
                    "id": album.albumartist
                }]
            }

        return {
            "type": "album",
            "id": str(album.id),
            "attributes": attributes,
            "relationships": relationships,
        }
Example #31
0
    def test_beetsdir_config(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('anoption: overwrite')

        config.read()
        self.assertEqual(config['anoption'].get(), 'overwrite')
Example #32
0
def audio_file(track_id):
    """Supply an audio file for the specified track.

    Args:
        track_id: The id of the track provided in the URL (integer).
    """
    track = current_app.config["lib"].get_item(track_id)
    if not track:
        return AURADocument.error(
            "404 Not Found",
            "No track with the requested id.",
            "There is no track with an id of {} in the library.".format(
                track_id),
        )

    path = py3_path(track.path)
    if not isfile(path):
        return AURADocument.error(
            "404 Not Found",
            "No audio file for the requested track.",
            ("There is no audio file for track {} at the expected location"
             ).format(track_id),
        )

    file_mimetype = guess_type(path)[0]
    if not file_mimetype:
        return AURADocument.error(
            "500 Internal Server Error",
            "Requested audio file has an unknown mimetype.",
            ("The audio file for track {} has an unknown mimetype. "
             "Its file extension is {}.").format(track_id,
                                                 path.split(".")[-1]),
        )

    # Check that the Accept header contains the file's mimetype
    # Takes into account */* and audio/*
    # Adding support for the bitrate parameter would require some effort so I
    # left it out. This means the client could be sent an error even if the
    # audio doesn't need transcoding.
    if not request.accept_mimetypes.best_match([file_mimetype]):
        return AURADocument.error(
            "406 Not Acceptable",
            "Unsupported MIME type or bitrate parameter in Accept header.",
            ("The audio file for track {} is only available as {} and "
             "bitrate parameters are not supported.").format(
                 track_id, file_mimetype),
        )

    return send_file(
        path,
        mimetype=file_mimetype,
        # Handles filename in Content-Disposition header
        as_attachment=True,
        # Tries to upgrade the stream to support range requests
        conditional=True,
    )
Example #33
0
def pil_deinterlace(path_in, path_out=None):
    path_out = path_out or temp_file_for(path_in)
    from PIL import Image

    try:
        im = Image.open(util.syspath(path_in))
        im.save(util.py3_path(path_out), progressive=False)
        return path_out
    except IOError:
        return path_in
Example #34
0
    def test_default_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        config.read()
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'library.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state.pickle'))
Example #35
0
    def test_default_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        config.read()
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'library.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state.pickle'))
Example #36
0
    def setUp(self):
        # A "clean" source list including only the defaults.
        beets.config.sources = []
        beets.config.read(user=False, defaults=True)

        # Direct paths to a temporary directory. Tests can also use this
        # temporary directory.
        self.temp_dir = util.bytestring_path(tempfile.mkdtemp())

        beets.config["statefile"] = util.py3_path(os.path.join(self.temp_dir, b"state.pickle"))
        beets.config["library"] = util.py3_path(os.path.join(self.temp_dir, b"library.db"))
        beets.config["directory"] = util.py3_path(os.path.join(self.temp_dir, b"libdir"))

        # Set $HOME, which is used by confit's `config_dir()` to create
        # directories.
        self._old_home = os.environ.get("HOME")
        os.environ["HOME"] = util.py3_path(self.temp_dir)

        # Initialize, but don't install, a DummyIO.
        self.io = DummyIO()
Example #37
0
    def test_cli_config_file_overwrites_beetsdir_defaults(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)
        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('anoption: value')

        cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
        with open(cli_config_path, 'w') as file:
            file.write('anoption: cli overwrite')
        self.run_command('--config', cli_config_path, 'test', lib=None)
        self.assertEqual(config['anoption'].get(), 'cli overwrite')
Example #38
0
    def test_cli_config_file_overwrites_beetsdir_defaults(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)
        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('anoption: value')

        cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
        with open(cli_config_path, 'w') as file:
            file.write('anoption: cli overwrite')
        self.run_command('--config', cli_config_path, 'test', lib=None)
        self.assertEqual(config['anoption'].get(), 'cli overwrite')
Example #39
0
    def convert_format(self, source, target, deinterlaced):
        from PIL import Image, UnidentifiedImageError

        try:
            with Image.open(syspath(source)) as im:
                im.save(py3_path(target), progressive=not deinterlaced)
                return target
        except (ValueError, TypeError, UnidentifiedImageError,
                FileNotFoundError, OSError):
            log.exception("failed to convert image {} -> {}", source, target)
            return source
Example #40
0
    def deinterlace(self, path_in, path_out=None):
        path_out = path_out or temp_file_for(path_in)
        from PIL import Image

        try:
            im = Image.open(syspath(path_in))
            im.save(py3_path(path_out), progressive=False)
            return path_out
        except IOError:
            # FIXME: Should probably issue a warning?
            return path_in
Example #41
0
    def setUp(self):
        self.setup_beets()

        self.item = self.add_item()
        config['smartplaylist']['playlists'].set([
            {'name': 'my_playlist.m3u',
             'query': self.item.title},
            {'name': 'all.m3u',
             'query': u''}
        ])
        config['smartplaylist']['playlist_dir'].set(py3_path(self.temp_dir))
        self.load_plugins('smartplaylist')
Example #42
0
 def _bpd_add(self, client, *items, **kwargs):
     """ Add the given item to the BPD playlist or queue.
     """
     paths = ['/'.join([
         item.artist, item.album,
         py3_path(os.path.basename(item.path))]) for item in items]
     playlist = kwargs.get('playlist')
     if playlist:
         commands = [('playlistadd', playlist, path) for path in paths]
     else:
         commands = [('add', path) for path in paths]
     responses = client.send_commands(*commands)
     self._assert_ok(*responses)
Example #43
0
 def _bpd_add(self, client, *items, **kwargs):
     """ Add the given item to the BPD playlist or queue.
     """
     paths = ['/'.join([
         item.artist, item.album,
         py3_path(os.path.basename(item.path))]) for item in items]
     playlist = kwargs.get('playlist')
     if playlist:
         commands = [('playlistadd', playlist, path) for path in paths]
     else:
         commands = [('add', path) for path in paths]
     responses = client.send_commands(*commands)
     self._assert_ok(*responses)
Example #44
0
    def setUp(self):
        self.setup_beets()

        self.item = self.add_item()
        config['smartplaylist']['playlists'].set([{
            'name': 'my_playlist.m3u',
            'query': self.item.title
        }, {
            'name': 'all.m3u',
            'query': u''
        }])
        config['smartplaylist']['playlist_dir'].set(py3_path(self.temp_dir))
        self.load_plugins('smartplaylist')
Example #45
0
    def setUp(self):
        # A "clean" source list including only the defaults.
        beets.config.sources = []
        beets.config.read(user=False, defaults=True)

        # Direct paths to a temporary directory. Tests can also use this
        # temporary directory.
        self.temp_dir = util.bytestring_path(tempfile.mkdtemp())

        beets.config['statefile'] = \
            util.py3_path(os.path.join(self.temp_dir, b'state.pickle'))
        beets.config['library'] = \
            util.py3_path(os.path.join(self.temp_dir, b'library.db'))
        beets.config['directory'] = \
            util.py3_path(os.path.join(self.temp_dir, b'libdir'))

        # Set $HOME, which is used by Confuse to create directories.
        self._old_home = os.environ.get('HOME')
        os.environ['HOME'] = util.py3_path(self.temp_dir)

        # Initialize, but don't install, a DummyIO.
        self.io = DummyIO()
Example #46
0
    def _create_connection(self):
        """Create a SQLite connection to the underlying database.

        Makes a new connection every time. If you need to configure the
        connection settings (e.g., add custom functions), override this
        method.
        """
        # Make a new connection. The `sqlite3` module can't use
        # bytestring paths here on Python 3, so we need to
        # provide a `str` using `py3_path`.
        conn = sqlite3.connect(py3_path(self.path), timeout=self.timeout)

        # Access SELECT results like dictionaries.
        conn.row_factory = sqlite3.Row
        return conn
Example #47
0
    def test_cli_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
        with open(cli_config_path, 'w') as file:
            file.write('library: beets.db\n')
            file.write('statefile: state')

        self.run_command('--config', cli_config_path, 'test', lib=None)
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'beets.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state'))
Example #48
0
    def test_beetsdir_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('library: beets.db\n')
            file.write('statefile: state')

        config.read()
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'beets.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state'))
Example #49
0
    def test_cli_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
        with open(cli_config_path, 'w') as file:
            file.write('library: beets.db\n')
            file.write('statefile: state')

        self.run_command('--config', cli_config_path, 'test', lib=None)
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'beets.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state'))
Example #50
0
    def test_beetsdir_config_paths_resolve_relative_to_beetsdir(self):
        os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)

        env_config_path = os.path.join(self.beetsdir, b'config.yaml')
        with open(env_config_path, 'w') as file:
            file.write('library: beets.db\n')
            file.write('statefile: state')

        config.read()
        self.assert_equal_path(
            util.bytestring_path(config['library'].as_filename()),
            os.path.join(self.beetsdir, b'beets.db'))
        self.assert_equal_path(
            util.bytestring_path(config['statefile'].as_filename()),
            os.path.join(self.beetsdir, b'state'))
Example #51
0
    def _create_connection(self):
        """Create a SQLite connection to the underlying database.

        Makes a new connection every time. If you need to configure the
        connection settings (e.g., add custom functions), override this
        method.
        """
        # Make a new connection. The `sqlite3` module can't use
        # bytestring paths here on Python 3, so we need to
        # provide a `str` using `py3_path`.
        conn = sqlite3.connect(
            py3_path(self.path), timeout=self.timeout
        )

        # Access SELECT results like dictionaries.
        conn.row_factory = sqlite3.Row
        return conn
Example #52
0
def pil_resize(maxwidth, path_in, path_out=None):
    """Resize using Python Imaging Library (PIL).  Return the output path
    of resized image.
    """
    path_out = path_out or temp_file_for(path_in)
    from PIL import Image
    log.debug(u'artresizer: PIL resizing {0} to {1}',
              util.displayable_path(path_in), util.displayable_path(path_out))

    try:
        im = Image.open(util.syspath(path_in))
        size = maxwidth, maxwidth
        im.thumbnail(size, Image.ANTIALIAS)
        im.save(util.py3_path(path_out))
        return path_out
    except IOError:
        log.error(u"PIL cannot create thumbnail for '{0}'",
                  util.displayable_path(path_in))
        return path_in
Example #53
0
File: db.py Project: Smyds/beets
    def _connection(self):
        """Get a SQLite connection object to the underlying database.
        One connection object is created per thread.
        """
        thread_id = threading.current_thread().ident
        with self._shared_map_lock:
            if thread_id in self._connections:
                return self._connections[thread_id]
            else:
                # Make a new connection. The `sqlite3` module can't use
                # bytestring paths here on Python 3, so we need to
                # provide a `str` using `py3_path`.
                conn = sqlite3.connect(
                    py3_path(self.path), timeout=self.timeout
                )

                # Access SELECT results like dictionaries.
                conn.row_factory = sqlite3.Row

                self._connections[thread_id] = conn
                return conn
Example #54
0
def _load_plugins(config):
    """Load the plugins specified in the configuration.
    """
    paths = config['pluginpath'].as_str_seq(split=False)
    paths = [util.normpath(p) for p in paths]
    log.debug(u'plugin paths: {0}', util.displayable_path(paths))

    # On Python 3, the search paths need to be unicode.
    paths = [util.py3_path(p) for p in paths]

    # Extend the `beetsplug` package to include the plugin paths.
    import beetsplug
    beetsplug.__path__ = paths + beetsplug.__path__

    # For backwards compatibility, also support plugin paths that
    # *contain* a `beetsplug` package.
    sys.path += paths

    plugins.load_plugins(config['plugins'].as_str_seq())
    plugins.send("pluginload")
    return plugins
Example #55
0
    def test_playlist_update(self):
        spl = SmartPlaylistPlugin()

        i = Mock(path=b'/tagada.mp3')
        i.evaluate_template.side_effect = \
            lambda pl, _: pl.replace(b'$title', b'ta:ga:da').decode()

        lib = Mock()
        lib.replacements = CHAR_REPLACE
        lib.items.return_value = [i]
        lib.albums.return_value = []

        q = Mock()
        a_q = Mock()
        pl = b'$title-my<playlist>.m3u', (q, None), (a_q, None)
        spl._matched_playlists = [pl]

        dir = bytestring_path(mkdtemp())
        config['smartplaylist']['relative_to'] = False
        config['smartplaylist']['playlist_dir'] = py3_path(dir)
        try:
            spl.update_playlists(lib)
        except Exception:
            rmtree(dir)
            raise

        lib.items.assert_called_once_with(q, None)
        lib.albums.assert_called_once_with(a_q, None)

        m3u_filepath = path.join(dir, b'ta_ga_da-my_playlist_.m3u')
        self.assertTrue(path.exists(m3u_filepath))
        with open(syspath(m3u_filepath), 'rb') as f:
            content = f.read()
        rmtree(dir)

        self.assertEqual(content, b'/tagada.mp3\n')
Example #56
0
File: db.py Project: beetbox/beets
    def _create_connection(self):
        """Create a SQLite connection to the underlying database.

        Makes a new connection every time. If you need to configure the
        connection settings (e.g., add custom functions), override this
        method.
        """
        # Make a new connection. The `sqlite3` module can't use
        # bytestring paths here on Python 3, so we need to
        # provide a `str` using `py3_path`.
        conn = sqlite3.connect(
            py3_path(self.path), timeout=self.timeout
        )

        if self.supports_extensions:
            conn.enable_load_extension(True)

            # Load any extension that are already loaded for other connections.
            for path in self._extensions:
                conn.load_extension(path)

        # Access SELECT results like dictionaries.
        conn.row_factory = sqlite3.Row
        return conn
Example #57
0
    def fetch_image(self, candidate, extra):
        """Downloads an image from a URL and checks whether it seems to
        actually be an image. If so, returns a path to the downloaded image.
        Otherwise, returns None.
        """
        if extra['maxwidth']:
            candidate.url = ArtResizer.shared.proxy_url(extra['maxwidth'],
                                                        candidate.url)
        try:
            with closing(self.request(candidate.url, stream=True,
                                      message=u'downloading image')) as resp:
                ct = resp.headers.get('Content-Type', None)

                # Download the image to a temporary file. As some servers
                # (notably fanart.tv) have proven to return wrong Content-Types
                # when images were uploaded with a bad file extension, do not
                # rely on it. Instead validate the type using the file magic
                # and only then determine the extension.
                data = resp.iter_content(chunk_size=1024)
                header = b''
                for chunk in data:
                    header += chunk
                    if len(header) >= 32:
                        # The imghdr module will only read 32 bytes, and our
                        # own additions in mediafile even less.
                        break
                else:
                    # server didn't return enough data, i.e. corrupt image
                    return

                real_ct = _image_mime_type(header)
                if real_ct is None:
                    # detection by file magic failed, fall back to the
                    # server-supplied Content-Type
                    # Is our type detection failsafe enough to drop this?
                    real_ct = ct

                if real_ct not in CONTENT_TYPES:
                    self._log.debug(u'not a supported image: {}',
                                    real_ct or u'unknown content type')
                    return

                ext = b'.' + CONTENT_TYPES[real_ct][0]

                if real_ct != ct:
                    self._log.warning(u'Server specified {}, but returned a '
                                      u'{} image. Correcting the extension '
                                      u'to {}',
                                      ct, real_ct, ext)

                suffix = py3_path(ext)
                with NamedTemporaryFile(suffix=suffix, delete=False) as fh:
                    # write the first already loaded part of the image
                    fh.write(header)
                    # download the remaining part of the image
                    for chunk in data:
                        fh.write(chunk)
                self._log.debug(u'downloaded art to: {0}',
                                util.displayable_path(fh.name))
                candidate.path = util.bytestring_path(fh.name)
                return

        except (IOError, requests.RequestException, TypeError) as exc:
            # Handling TypeError works around a urllib3 bug:
            # https://github.com/shazow/urllib3/issues/556
            self._log.debug(u'error fetching art: {}', exc)
            return
Example #58
0
 def test_beetsdir_points_to_file_error(self):
     beetsdir = os.path.join(self.temp_dir, b'beetsfile')
     open(beetsdir, 'a').close()
     os.environ['BEETSDIR'] = util.py3_path(beetsdir)
     self.assertRaises(ConfigError, self.run_command, 'test')