Exemple #1
0
    def test_gets_instance(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.resolve.return_value = DummyExtension()

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #2
0
def main():
    settings = load()
    if not settings:
        return

    config = convert(settings)

    known = [
        'spotify', 'scrobbler', 'mpd', 'mpris', 'local', 'stream', 'http']
    extensions = [e for e in ext.load_extensions() if e.ext_name in known]

    print b'Converted config:\n'
    print config_lib.format(config, extensions)

    conf_file = path.expand_path(b'$XDG_CONFIG_DIR/mopidy/mopidy.conf')
    if os.path.exists(conf_file):
        print '%s exists, exiting.' % conf_file
        sys.exit(1)

    print 'Write new config to %s? [yN]' % conf_file,
    if raw_input() != 'y':
        print 'Not saving, exiting.'
        sys.exit(0)

    serialized_config = config_lib.format(config, extensions, display=False)
    with io.open(conf_file, 'wb') as filehandle:
        filehandle.write(serialized_config)
    print 'Done.'
Exemple #3
0
def main():
    settings = load()
    if not settings:
        return

    config = convert(settings)

    known = ["spotify", "scrobbler", "mpd", "mpris", "local", "stream", "http"]
    extensions = [e for e in ext.load_extensions() if e.ext_name in known]

    print(b"Converted config:\n")
    print(config_lib.format(config, extensions))

    conf_file = path.expand_path(b"$XDG_CONFIG_DIR/mopidy/mopidy.conf")
    if os.path.exists(conf_file):
        print("%s exists, exiting." % conf_file)
        sys.exit(1)

    print("Write new config to %s? [yN]" % conf_file, end=" ")
    if raw_input() != "y":
        print("Not saving, exiting.")
        sys.exit(0)

    serialized_config = config_lib.format(config, extensions, display=False)
    with io.open(conf_file, "wb") as filehandle:
        filehandle.write(serialized_config)
    print("Done.")
Exemple #4
0
def main():
    signal.signal(signal.SIGTERM, process.exit_handler)
    signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    args = commands.parser.parse_args(args=mopidy_args)
    if args.show_config:
        commands.show_config(args)
    if args.show_deps:
        commands.show_deps()

    # TODO: figure out a way to make the boilerplate in this file reusable in
    # scanner and other places we need it.

    try:
        # Initial config without extensions to bootstrap logging.
        logging_initialized = False
        logging_config, _ = config_lib.load(args.config_files, [],
                                            args.config_overrides)

        # TODO: setup_logging needs defaults in-case config values are None
        log.setup_logging(logging_config, args.verbosity_level,
                          args.save_debug_log)
        logging_initialized = True

        create_file_structures()
        check_old_locations()

        installed_extensions = ext.load_extensions()

        config, config_errors = config_lib.load(args.config_files,
                                                installed_extensions,
                                                args.config_overrides)

        # Filter out disabled extensions and remove any config errors for them.
        enabled_extensions = []
        for extension in installed_extensions:
            enabled = config[extension.ext_name]['enabled']
            if ext.validate_extension(extension) and enabled:
                enabled_extensions.append(extension)
            elif extension.ext_name in config_errors:
                del config_errors[extension.ext_name]

        log_extension_info(installed_extensions, enabled_extensions)
        check_config_errors(config_errors)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        log.setup_log_levels(proxied_config)
        ext.register_gstreamer_elements(enabled_extensions)

        # Anything that wants to exit after this point must use
        # mopidy.utils.process.exit_process as actors have been started.
        start(proxied_config, enabled_extensions)
    except KeyboardInterrupt:
        pass
    except Exception as ex:
        if logging_initialized:
            logger.exception(ex)
        raise
Exemple #5
0
    def test_gets_instance(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.load.return_value = DummyExtension()

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #6
0
def main():
    signal.signal(signal.SIGTERM, process.exit_handler)
    signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    args = commands.parser.parse_args(args=mopidy_args)
    if args.show_config:
        commands.show_config(args)
    if args.show_deps:
        commands.show_deps()

    # TODO: figure out a way to make the boilerplate in this file reusable in
    # scanner and other places we need it.

    try:
        # Initial config without extensions to bootstrap logging.
        logging_initialized = False
        logging_config, _ = config_lib.load(
            args.config_files, [], args.config_overrides)

        # TODO: setup_logging needs defaults in-case config values are None
        log.setup_logging(
            logging_config, args.verbosity_level, args.save_debug_log)
        logging_initialized = True

        create_file_structures()
        check_old_locations()

        installed_extensions = ext.load_extensions()

        config, config_errors = config_lib.load(
            args.config_files, installed_extensions, args.config_overrides)

        # Filter out disabled extensions and remove any config errors for them.
        enabled_extensions = []
        for extension in installed_extensions:
            enabled = config[extension.ext_name]['enabled']
            if ext.validate_extension(extension) and enabled:
                enabled_extensions.append(extension)
            elif extension.ext_name in config_errors:
                del config_errors[extension.ext_name]

        log_extension_info(installed_extensions, enabled_extensions)
        check_config_errors(config_errors)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        log.setup_log_levels(proxied_config)
        ext.register_gstreamer_elements(enabled_extensions)

        # Anything that wants to exit after this point must use
        # mopidy.utils.process.exit_process as actors have been started.
        start(proxied_config, enabled_extensions)
    except KeyboardInterrupt:
        pass
    except Exception as ex:
        if logging_initialized:
            logger.exception(ex)
        raise
Exemple #7
0
    def test_creating_instance_fails(self, iter_entry_points_mock):
        mock_extension = mock.Mock(spec=ext.Extension)
        mock_extension.side_effect = Exception

        mock_entry_point = mock.Mock()
        mock_entry_point.load.return_value = mock_extension

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #8
0
    def test_creating_instance_fails(self, iter_entry_points_mock):
        mock_extension = mock.Mock(spec=ext.Extension)
        mock_extension.side_effect = Exception

        mock_entry_point = mock.Mock()
        mock_entry_point.resolve.return_value = mock_extension

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #9
0
    def test_gets_wrong_class(self, iter_entry_points_mock):
        class WrongClass:
            pass

        mock_entry_point = mock.Mock()
        mock_entry_point.resolve.return_value = WrongClass

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #10
0
def main():
    options = parse_options()
    # TODO: support config files and overrides (shared from main?)
    config_files = [
        b'/etc/mopidy/mopidy.conf', b'$XDG_CONFIG_DIR/mopidy/mopidy.conf'
    ]
    config_overrides = []

    # TODO: decide if we want to avoid this boilerplate some how.
    # Initial config without extensions to bootstrap logging.
    logging_config, _ = config_lib.load(config_files, [], config_overrides)
    log.setup_root_logger()
    log.setup_console_logging(logging_config, options.verbosity_level)

    extensions = ext.load_extensions()
    config, errors = config_lib.load(config_files, extensions,
                                     config_overrides)
    log.setup_log_levels(config)

    if not config['local']['media_dir']:
        logging.warning('Config value local/media_dir is not set.')
        return

    # TODO: missing error checking and other default setup code.

    tracks = []

    def store(data):
        track = translator(data)
        tracks.append(track)
        logging.debug('Added %s', track.uri)

    def debug(uri, error, debug):
        logging.warning('Failed %s: %s', uri, error)
        logging.debug('Debug info for %s: %s', uri, debug)

    logging.info('Scanning %s', config['local']['media_dir'])

    scanner = Scanner(config['local']['media_dir'], store, debug)
    try:
        scanner.start()
    except KeyboardInterrupt:
        scanner.stop()

    logging.info('Done scanning; writing tag cache...')

    for row in mpd_translator.tracks_to_tag_cache_format(
            tracks, config['local']['media_dir']):
        if len(row) == 1:
            print('%s' % row).encode('utf-8')
        else:
            print('%s: %s' % row).encode('utf-8')

    logging.info('Done writing tag cache')
Exemple #11
0
    def test_get_command_fails(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.load.return_value = DummyExtension

        iter_entry_points_mock.return_value = [mock_entry_point]

        with mock.patch.object(DummyExtension, 'get_command') as get:
            get.side_effect = Exception

            assert ext.load_extensions() == []
            get.assert_called_once_with()
Exemple #12
0
    def test_load_extensions(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.resolve.return_value = DummyExtension

        iter_entry_points_mock.return_value = [mock_entry_point]

        expected = ext.ExtensionData(
            any_testextension, mock_entry_point, IsA(config.ConfigSchema),
            any_unicode, None)

        assert ext.load_extensions() == [expected]
Exemple #13
0
    def test_get_command_fails(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.resolve.return_value = DummyExtension

        iter_entry_points_mock.return_value = [mock_entry_point]

        with mock.patch.object(DummyExtension, "get_command") as get:
            get.side_effect = Exception

            assert ext.load_extensions() == []
            get.assert_called_once_with()
Exemple #14
0
    def test_load_extensions(self, iter_entry_points_mock):
        mock_entry_point = mock.Mock()
        mock_entry_point.load.return_value = DummyExtension

        iter_entry_points_mock.return_value = [mock_entry_point]

        expected = ext.ExtensionData(
            any_testextension, mock_entry_point, IsA(config.ConfigSchema),
            any_unicode, None)

        assert ext.load_extensions() == [expected]
Exemple #15
0
    def test_gets_wrong_class(self, iter_entry_points_mock):

        class WrongClass(object):
            pass

        mock_entry_point = mock.Mock()
        mock_entry_point.load.return_value = WrongClass

        iter_entry_points_mock.return_value = [mock_entry_point]

        assert ext.load_extensions() == []
Exemple #16
0
def main():
    options = parse_options()
    # TODO: support config files and overrides (shared from main?)
    config_files = [b'/etc/mopidy/mopidy.conf',
                    b'$XDG_CONFIG_DIR/mopidy/mopidy.conf']
    config_overrides = []

    # TODO: decide if we want to avoid this boilerplate some how.
    # Initial config without extensions to bootstrap logging.
    logging_config, _ = config_lib.load(config_files, [], config_overrides)
    log.setup_root_logger()
    log.setup_console_logging(logging_config, options.verbosity_level)

    extensions = ext.load_extensions()
    config, errors = config_lib.load(
        config_files, extensions, config_overrides)
    log.setup_log_levels(config)

    if not config['local']['media_dir']:
        logging.warning('Config value local/media_dir is not set.')
        return

    # TODO: missing error checking and other default setup code.

    tracks = []

    def store(data):
        track = translator(data)
        tracks.append(track)
        logging.debug('Added %s', track.uri)

    def debug(uri, error, debug):
        logging.warning('Failed %s: %s', uri, error)
        logging.debug('Debug info for %s: %s', uri, debug)

    logging.info('Scanning %s', config['local']['media_dir'])

    scanner = Scanner(config['local']['media_dir'], store, debug)
    try:
        scanner.start()
    except KeyboardInterrupt:
        scanner.stop()

    logging.info('Done scanning; writing tag cache...')

    for row in mpd_translator.tracks_to_tag_cache_format(
            tracks, config['local']['media_dir']):
        if len(row) == 1:
            print ('%s' % row).encode('utf-8')
        else:
            print ('%s: %s' % row).encode('utf-8')

    logging.info('Done writing tag cache')
Exemple #17
0
def show_config(args):
    """Prints the effective config and exits."""
    extensions = ext.load_extensions()
    config, errors = config_lib.load(
        args.config_files, extensions, args.config_overrides)

    # Clear out any config for disabled extensions.
    for extension in extensions:
        if not ext.validate_extension(extension):
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled itself.'}
        elif not config[extension.ext_name]['enabled']:
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled by config.'}

    print config_lib.format(config, extensions, errors)
    sys.exit(0)
Exemple #18
0
def show_config(args):
    """Prints the effective config and exits."""
    extensions = ext.load_extensions()
    config, errors = config_lib.load(args.config_files, extensions,
                                     args.config_overrides)

    # Clear out any config for disabled extensions.
    for extension in extensions:
        if not ext.validate_extension(extension):
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled itself.'
            }
        elif not config[extension.ext_name]['enabled']:
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled by config.'
            }

    print config_lib.format(config, extensions, errors)
    sys.exit(0)
Exemple #19
0
def show_config_callback(option, opt, value, parser):
    # TODO: don't use callback for this as --config or -o set after
    # --show-config will be ignored.
    files = getattr(parser.values, 'config', b'').split(b':')
    overrides = getattr(parser.values, 'overrides', [])

    extensions = ext.load_extensions()
    config, errors = config_lib.load(files, extensions, overrides)

    # Clear out any config for disabled extensions.
    for extension in extensions:
        if not ext.validate_extension(extension):
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled its self.'}
        elif not config[extension.ext_name]['enabled']:
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled by config.'}

    print config_lib.format(config, extensions, errors)
    sys.exit(0)
Exemple #20
0
def show_config_callback(option, opt, value, parser):
    # TODO: don't use callback for this as --config or -o set after
    # --show-config will be ignored.
    files = getattr(parser.values, 'config', b'').split(b':')
    overrides = getattr(parser.values, 'overrides', [])

    extensions = ext.load_extensions()
    config, errors = config_lib.load(files, extensions, overrides)

    # Clear out any config for disabled extensions.
    for extension in extensions:
        if not ext.validate_extension(extension):
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled its self.'
            }
        elif not config[extension.ext_name]['enabled']:
            config[extension.ext_name] = {b'enabled': False}
            errors[extension.ext_name] = {
                b'enabled': b'extension disabled by config.'
            }

    print config_lib.format(config, extensions, errors)
    sys.exit(0)
Exemple #21
0
def main():
    log.bootstrap_delayed_logging()
    logger.info(f"Starting Mopidy {versioning.get_version()}")

    signal.signal(signal.SIGTERM, process.sigterm_handler)
    # Windows does not have signal.SIGUSR1
    if hasattr(signal, "SIGUSR1"):
        signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    try:
        registry = ext.Registry()

        root_cmd = commands.RootCommand()
        config_cmd = commands.ConfigCommand()
        deps_cmd = commands.DepsCommand()

        root_cmd.set(extension=None, registry=registry)
        root_cmd.add_child("config", config_cmd)
        root_cmd.add_child("deps", deps_cmd)

        extensions_data = ext.load_extensions()

        for data in extensions_data:
            if data.command:  # TODO: check isinstance?
                data.command.set(extension=data.extension)
                root_cmd.add_child(data.extension.ext_name, data.command)

        args = root_cmd.parse(sys.argv[1:])

        config, config_errors = config_lib.load(
            args.config_files,
            [d.config_schema for d in extensions_data],
            [d.config_defaults for d in extensions_data],
            args.config_overrides,
        )

        create_core_dirs(config)
        create_initial_config_file(args, extensions_data)

        log.setup_logging(config, args.base_verbosity_level,
                          args.verbosity_level)

        extensions = {
            "validate": [],
            "config": [],
            "disabled": [],
            "enabled": [],
        }
        for data in extensions_data:
            extension = data.extension

            # TODO: factor out all of this to a helper that can be tested
            if not ext.validate_extension_data(data):
                config[extension.ext_name] = {"enabled": False}
                config_errors[extension.ext_name] = {
                    "enabled": "extension disabled by self check."
                }
                extensions["validate"].append(extension)
            elif not config[extension.ext_name]["enabled"]:
                config[extension.ext_name] = {"enabled": False}
                config_errors[extension.ext_name] = {
                    "enabled": "extension disabled by user config."
                }
                extensions["disabled"].append(extension)
            elif config_errors.get(extension.ext_name):
                config[extension.ext_name]["enabled"] = False
                config_errors[extension.ext_name][
                    "enabled"] = "extension disabled due to config errors."
                extensions["config"].append(extension)
            else:
                extensions["enabled"].append(extension)

        log_extension_info([d.extension for d in extensions_data],
                           extensions["enabled"])

        # Config and deps commands are simply special cased for now.
        if args.command == config_cmd:
            schemas = [d.config_schema for d in extensions_data]
            return args.command.run(config, config_errors, schemas)
        elif args.command == deps_cmd:
            return args.command.run()

        check_config_errors(config, config_errors, extensions)

        if not extensions["enabled"]:
            logger.error("No extension enabled, exiting...")
            sys.exit(1)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        if args.extension and args.extension not in extensions["enabled"]:
            logger.error(
                "Unable to run command provided by disabled extension %s",
                args.extension.ext_name,
            )
            return 1

        for extension in extensions["enabled"]:
            try:
                extension.setup(registry)
            except Exception:
                # TODO: would be nice a transactional registry. But sadly this
                # is a bit tricky since our current API is giving out a mutable
                # list. We might however be able to replace this with a
                # collections.Sequence to provide a RO view.
                logger.exception(
                    f"Extension {extension.ext_name} failed during setup. "
                    f"This might have left the registry in a bad state.")

        # Anything that wants to exit after this point must use
        # mopidy.internal.process.exit_process as actors can have been started.
        try:
            return args.command.run(args, proxied_config)
        except NotImplementedError:
            print(root_cmd.format_help())
            return 1

    except KeyboardInterrupt:
        pass
    except Exception as ex:
        logger.exception(ex)
        raise
Exemple #22
0
def main():
    log.bootstrap_delayed_logging()
    logger.info('Starting Mopidy %s', versioning.get_version())

    signal.signal(signal.SIGTERM, process.exit_handler)
    # Windows does not have signal.SIGUSR1
    if hasattr(signal, 'SIGUSR1'):
        signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    try:
        registry = ext.Registry()

        root_cmd = commands.RootCommand()
        config_cmd = commands.ConfigCommand()
        deps_cmd = commands.DepsCommand()

        root_cmd.set(extension=None, registry=registry)
        root_cmd.add_child('config', config_cmd)
        root_cmd.add_child('deps', deps_cmd)

        installed_extensions = ext.load_extensions()

        for extension in installed_extensions:
            ext_cmd = extension.get_command()
            if ext_cmd:
                ext_cmd.set(extension=extension)
                root_cmd.add_child(extension.ext_name, ext_cmd)

        args = root_cmd.parse(mopidy_args)

        create_file_structures_and_config(args, installed_extensions)
        check_old_locations()

        config, config_errors = config_lib.load(
            args.config_files, installed_extensions, args.config_overrides)

        verbosity_level = args.base_verbosity_level
        if args.verbosity_level:
            verbosity_level += args.verbosity_level

        log.setup_logging(config, verbosity_level, args.save_debug_log)

        extensions = {
            'validate': [], 'config': [], 'disabled': [], 'enabled': []}
        for extension in installed_extensions:
            if not ext.validate_extension(extension):
                config[extension.ext_name] = {'enabled': False}
                config_errors[extension.ext_name] = {
                    'enabled': 'extension disabled by self check.'}
                extensions['validate'].append(extension)
            elif not config[extension.ext_name]['enabled']:
                config[extension.ext_name] = {'enabled': False}
                config_errors[extension.ext_name] = {
                    'enabled': 'extension disabled by user config.'}
                extensions['disabled'].append(extension)
            elif config_errors.get(extension.ext_name):
                config[extension.ext_name]['enabled'] = False
                config_errors[extension.ext_name]['enabled'] = (
                    'extension disabled due to config errors.')
                extensions['config'].append(extension)
            else:
                extensions['enabled'].append(extension)

        log_extension_info(installed_extensions, extensions['enabled'])

        # Config and deps commands are simply special cased for now.
        if args.command == config_cmd:
            return args.command.run(
                config, config_errors, installed_extensions)
        elif args.command == deps_cmd:
            return args.command.run()

        check_config_errors(config, config_errors, extensions)

        if not extensions['enabled']:
            logger.error('No extension enabled, exiting...')
            sys.exit(1)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        if args.extension and args.extension not in extensions['enabled']:
            logger.error(
                'Unable to run command provided by disabled extension %s',
                args.extension.ext_name)
            return 1

        for extension in extensions['enabled']:
            extension.setup(registry)

        # Anything that wants to exit after this point must use
        # mopidy.utils.process.exit_process as actors can have been started.
        try:
            return args.command.run(args, proxied_config)
        except NotImplementedError:
            print(root_cmd.format_help())
            return 1

    except KeyboardInterrupt:
        pass
    except Exception as ex:
        logger.exception(ex)
        raise
Exemple #23
0
def main():
    signal.signal(signal.SIGTERM, process.exit_handler)
    signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    loop = gobject.MainLoop()
    options = parse_options()
    config_files = options.config.split(b':')
    config_overrides = options.overrides

    enabled_extensions = []  # Make sure it is defined before the finally block
    logging_initialized = False

    # TODO: figure out a way to make the boilerplate in this file reusable in
    # scanner and other places we need it.

    try:
        # Initial config without extensions to bootstrap logging.
        logging_config, _ = config_lib.load(config_files, [], config_overrides)

        # TODO: setup_logging needs defaults in-case config values are None
        log.setup_logging(
            logging_config, options.verbosity_level, options.save_debug_log)
        logging_initialized = True

        installed_extensions = ext.load_extensions()

        # TODO: wrap config in RO proxy.
        config, config_errors = config_lib.load(
            config_files, installed_extensions, config_overrides)

        # Filter out disabled extensions and remove any config errors for them.
        for extension in installed_extensions:
            enabled = config[extension.ext_name]['enabled']
            if ext.validate_extension(extension) and enabled:
                enabled_extensions.append(extension)
            elif extension.ext_name in config_errors:
                del config_errors[extension.ext_name]

        log_extension_info(installed_extensions, enabled_extensions)
        check_config_errors(config_errors)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        log.setup_log_levels(proxied_config)
        create_file_structures()
        check_old_locations()
        ext.register_gstreamer_elements(enabled_extensions)

        # Anything that wants to exit after this point must use
        # mopidy.utils.process.exit_process as actors have been started.
        audio = setup_audio(proxied_config)
        backends = setup_backends(proxied_config, enabled_extensions, audio)
        core = setup_core(audio, backends)
        setup_frontends(proxied_config, enabled_extensions, core)
        loop.run()
    except KeyboardInterrupt:
        if logging_initialized:
            logger.info('Interrupted. Exiting...')
    except Exception as ex:
        if logging_initialized:
            logger.exception(ex)
        raise
    finally:
        loop.quit()
        stop_frontends(enabled_extensions)
        stop_core()
        stop_backends(enabled_extensions)
        stop_audio()
        process.stop_remaining_actors()
Exemple #24
0
def main():
    log.bootstrap_delayed_logging()
    logger.info("Starting Mopidy %s", versioning.get_version())

    signal.signal(signal.SIGTERM, process.sigterm_handler)
    # Windows does not have signal.SIGUSR1
    if hasattr(signal, "SIGUSR1"):
        signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    try:
        registry = ext.Registry()

        root_cmd = commands.RootCommand()
        config_cmd = commands.ConfigCommand()
        deps_cmd = commands.DepsCommand()

        root_cmd.set(extension=None, registry=registry)
        root_cmd.add_child("config", config_cmd)
        root_cmd.add_child("deps", deps_cmd)

        extensions_data = ext.load_extensions()

        for data in extensions_data:
            if data.command:  # TODO: check isinstance?
                data.command.set(extension=data.extension)
                root_cmd.add_child(data.extension.ext_name, data.command)

        args = root_cmd.parse(sys.argv[1:])

        config, config_errors = config_lib.load(
            args.config_files,
            [d.config_schema for d in extensions_data],
            [d.config_defaults for d in extensions_data],
            args.config_overrides,
        )

        create_core_dirs(config)
        create_initial_config_file(args, extensions_data)

        verbosity_level = args.base_verbosity_level
        if args.verbosity_level:
            verbosity_level += args.verbosity_level

        log.setup_logging(config, verbosity_level, args.save_debug_log)

        extensions = {"validate": [], "config": [], "disabled": [], "enabled": []}
        for data in extensions_data:
            extension = data.extension

            # TODO: factor out all of this to a helper that can be tested
            if not ext.validate_extension_data(data):
                config[extension.ext_name] = {"enabled": False}
                config_errors[extension.ext_name] = {"enabled": "extension disabled by self check."}
                extensions["validate"].append(extension)
            elif not config[extension.ext_name]["enabled"]:
                config[extension.ext_name] = {"enabled": False}
                config_errors[extension.ext_name] = {"enabled": "extension disabled by user config."}
                extensions["disabled"].append(extension)
            elif config_errors.get(extension.ext_name):
                config[extension.ext_name]["enabled"] = False
                config_errors[extension.ext_name]["enabled"] = "extension disabled due to config errors."
                extensions["config"].append(extension)
            else:
                extensions["enabled"].append(extension)

        log_extension_info([d.extension for d in extensions_data], extensions["enabled"])

        # Config and deps commands are simply special cased for now.
        if args.command == config_cmd:
            schemas = [d.config_schema for d in extensions_data]
            return args.command.run(config, config_errors, schemas)
        elif args.command == deps_cmd:
            return args.command.run()

        check_config_errors(config, config_errors, extensions)

        if not extensions["enabled"]:
            logger.error("No extension enabled, exiting...")
            sys.exit(1)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        if args.extension and args.extension not in extensions["enabled"]:
            logger.error("Unable to run command provided by disabled extension %s", args.extension.ext_name)
            return 1

        for extension in extensions["enabled"]:
            try:
                extension.setup(registry)
            except Exception:
                # TODO: would be nice a transactional registry. But sadly this
                # is a bit tricky since our current API is giving out a mutable
                # list. We might however be able to replace this with a
                # collections.Sequence to provide a RO view.
                logger.exception(
                    "Extension %s failed during setup, this might" " have left the registry in a bad state.",
                    extension.ext_name,
                )

        # Anything that wants to exit after this point must use
        # mopidy.internal.process.exit_process as actors can have been started.
        try:
            return args.command.run(args, proxied_config)
        except NotImplementedError:
            print(root_cmd.format_help())
            return 1

    except KeyboardInterrupt:
        pass
    except Exception as ex:
        logger.exception(ex)
        raise
Exemple #25
0
def main():
    args = parse_args()
    # TODO: support config files and overrides (shared from main?)
    config_files = [
        b'/etc/mopidy/mopidy.conf', b'$XDG_CONFIG_DIR/mopidy/mopidy.conf'
    ]
    config_overrides = []

    # TODO: decide if we want to avoid this boilerplate some how.
    # Initial config without extensions to bootstrap logging.
    logging_config, _ = config_lib.load(config_files, [], config_overrides)
    log.setup_root_logger()
    log.setup_console_logging(logging_config, args.verbosity_level)

    extensions = ext.load_extensions()
    config, errors = config_lib.load(config_files, extensions,
                                     config_overrides)
    log.setup_log_levels(config)

    if not config['local']['media_dir']:
        logging.warning('Config value local/media_dir is not set.')
        return

    if not config['local']['scan_timeout']:
        logging.warning('Config value local/scan_timeout is not set.')
        return

    # TODO: missing config error checking and other default setup code.

    updaters = {}
    for e in extensions:
        for updater_class in e.get_library_updaters():
            if updater_class and 'local' in updater_class.uri_schemes:
                updaters[e.ext_name] = updater_class

    if not updaters:
        logging.error('No usable library updaters found.')
        return
    elif len(updaters) > 1:
        logging.error(
            'More than one library updater found. '
            'Provided by: %s', ', '.join(updaters.keys()))
        return

    local_updater = updaters.values()[0](config)  # TODO: switch to actor?

    media_dir = config['local']['media_dir']
    excluded_extensions = config['local']['excluded_file_extensions']

    uris_library = set()
    uris_update = set()
    uris_remove = set()

    logging.info('Checking tracks from library.')
    for track in local_updater.load():
        try:
            stat = os.stat(path.uri_to_path(track.uri))
            if int(stat.st_mtime) > track.last_modified:
                uris_update.add(track.uri)
            uris_library.add(track.uri)
        except OSError:
            uris_remove.add(track.uri)

    logging.info('Removing %d moved or deleted tracks.', len(uris_remove))
    for uri in uris_remove:
        local_updater.remove(uri)

    logging.info('Checking %s for new or modified tracks.', media_dir)
    for uri in path.find_uris(config['local']['media_dir']):
        if os.path.splitext(path.uri_to_path(uri))[1] in excluded_extensions:
            logging.debug('Skipped %s: File extension excluded.', uri)
            continue

        if uri not in uris_library:
            uris_update.add(uri)

    logging.info('Found %d new or modified tracks.', len(uris_update))
    logging.info('Scanning new and modified tracks.')

    scanner = Scanner(config['local']['scan_timeout'])
    for uri in uris_update:
        try:
            data = scanner.scan(uri)
            data[b'mtime'] = os.path.getmtime(path.uri_to_path(uri))
            track = translator(data)
            local_updater.add(track)
            logging.debug('Added %s', track.uri)
        except exceptions.ScannerError as error:
            logging.warning('Failed %s: %s', uri, error)

    logging.info('Done scanning; commiting changes.')
    local_updater.commit()
Exemple #26
0
 def test_no_extensions(self, iter_entry_points_mock):
     iter_entry_points_mock.return_value = []
     assert ext.load_extensions() == []
Exemple #27
0
def main():
    log.bootstrap_delayed_logging()
    logger.info('Starting Mopidy %s', versioning.get_version())

    signal.signal(signal.SIGTERM, process.exit_handler)
    # Windows does not have signal.SIGUSR1
    if hasattr(signal, 'SIGUSR1'):
        signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    try:
        registry = ext.Registry()

        root_cmd = commands.RootCommand()
        config_cmd = commands.ConfigCommand()
        deps_cmd = commands.DepsCommand()

        root_cmd.set(extension=None, registry=registry)
        root_cmd.add_child('config', config_cmd)
        root_cmd.add_child('deps', deps_cmd)

        extensions_data = ext.load_extensions()

        for data in extensions_data:
            if data.command:  # TODO: check isinstance?
                data.command.set(extension=data.extension)
                root_cmd.add_child(data.extension.ext_name, data.command)

        args = root_cmd.parse(mopidy_args)

        create_file_structures_and_config(args, extensions_data)
        check_old_locations()

        config, config_errors = config_lib.load(
            args.config_files, [d.config_schema for d in extensions_data],
            [d.config_defaults
             for d in extensions_data], args.config_overrides)

        verbosity_level = args.base_verbosity_level
        if args.verbosity_level:
            verbosity_level += args.verbosity_level

        log.setup_logging(config, verbosity_level, args.save_debug_log)

        extensions = {
            'validate': [],
            'config': [],
            'disabled': [],
            'enabled': []
        }
        for data in extensions_data:
            extension = data.extension

            # TODO: factor out all of this to a helper that can be tested
            if not ext.validate_extension_data(data):
                config[extension.ext_name] = {'enabled': False}
                config_errors[extension.ext_name] = {
                    'enabled': 'extension disabled by self check.'
                }
                extensions['validate'].append(extension)
            elif not config[extension.ext_name]['enabled']:
                config[extension.ext_name] = {'enabled': False}
                config_errors[extension.ext_name] = {
                    'enabled': 'extension disabled by user config.'
                }
                extensions['disabled'].append(extension)
            elif config_errors.get(extension.ext_name):
                config[extension.ext_name]['enabled'] = False
                config_errors[extension.ext_name]['enabled'] = (
                    'extension disabled due to config errors.')
                extensions['config'].append(extension)
            else:
                extensions['enabled'].append(extension)

        log_extension_info([d.extension for d in extensions_data],
                           extensions['enabled'])

        # Config and deps commands are simply special cased for now.
        if args.command == config_cmd:
            schemas = [d.config_schema for d in extensions_data]
            return args.command.run(config, config_errors, schemas)
        elif args.command == deps_cmd:
            return args.command.run()

        check_config_errors(config, config_errors, extensions)

        if not extensions['enabled']:
            logger.error('No extension enabled, exiting...')
            sys.exit(1)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        if args.extension and args.extension not in extensions['enabled']:
            logger.error(
                'Unable to run command provided by disabled extension %s',
                args.extension.ext_name)
            return 1

        for extension in extensions['enabled']:
            try:
                extension.setup(registry)
            except Exception:
                # TODO: would be nice a transactional registry. But sadly this
                # is a bit tricky since our current API is giving out a mutable
                # list. We might however be able to replace this with a
                # collections.Sequence to provide a RO view.
                logger.exception(
                    'Extension %s failed during setup, this might'
                    ' have left the registry in a bad state.',
                    extension.ext_name)

        # Anything that wants to exit after this point must use
        # mopidy.internal.process.exit_process as actors can have been started.
        try:
            return args.command.run(args, proxied_config)
        except NotImplementedError:
            print(root_cmd.format_help())
            return 1

    except KeyboardInterrupt:
        pass
    except Exception as ex:
        logger.exception(ex)
        raise
Exemple #28
0
def main():
    signal.signal(signal.SIGTERM, process.exit_handler)
    signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)

    loop = gobject.MainLoop()
    options = parse_options()
    config_files = options.config.split(b':')
    config_overrides = options.overrides

    enabled_extensions = []  # Make sure it is defined before the finally block
    logging_initialized = False

    # TODO: figure out a way to make the boilerplate in this file reusable in
    # scanner and other places we need it.

    try:
        # Initial config without extensions to bootstrap logging.
        logging_config, _ = config_lib.load(config_files, [], config_overrides)

        # TODO: setup_logging needs defaults in-case config values are None
        log.setup_logging(logging_config, options.verbosity_level,
                          options.save_debug_log)
        logging_initialized = True

        installed_extensions = ext.load_extensions()

        # TODO: wrap config in RO proxy.
        config, config_errors = config_lib.load(config_files,
                                                installed_extensions,
                                                config_overrides)

        # Filter out disabled extensions and remove any config errors for them.
        for extension in installed_extensions:
            enabled = config[extension.ext_name]['enabled']
            if ext.validate_extension(extension) and enabled:
                enabled_extensions.append(extension)
            elif extension.ext_name in config_errors:
                del config_errors[extension.ext_name]

        log_extension_info(installed_extensions, enabled_extensions)
        check_config_errors(config_errors)

        # Read-only config from here on, please.
        proxied_config = config_lib.Proxy(config)

        log.setup_log_levels(proxied_config)
        create_file_structures()
        check_old_locations()
        ext.register_gstreamer_elements(enabled_extensions)

        # Anything that wants to exit after this point must use
        # mopidy.utils.process.exit_process as actors have been started.
        audio = setup_audio(proxied_config)
        backends = setup_backends(proxied_config, enabled_extensions, audio)
        core = setup_core(audio, backends)
        setup_frontends(proxied_config, enabled_extensions, core)
        loop.run()
    except KeyboardInterrupt:
        if logging_initialized:
            logger.info('Interrupted. Exiting...')
    except Exception as ex:
        if logging_initialized:
            logger.exception(ex)
        raise
    finally:
        loop.quit()
        stop_frontends(enabled_extensions)
        stop_core()
        stop_backends(enabled_extensions)
        stop_audio()
        process.stop_remaining_actors()
Exemple #29
0
def main():
    args = parse_args()
    # TODO: support config files and overrides (shared from main?)
    config_files = [b'/etc/mopidy/mopidy.conf',
                    b'$XDG_CONFIG_DIR/mopidy/mopidy.conf']
    config_overrides = []

    # TODO: decide if we want to avoid this boilerplate some how.
    # Initial config without extensions to bootstrap logging.
    logging_config, _ = config_lib.load(config_files, [], config_overrides)
    log.setup_root_logger()
    log.setup_console_logging(logging_config, args.verbosity_level)

    extensions = ext.load_extensions()
    config, errors = config_lib.load(
        config_files, extensions, config_overrides)
    log.setup_log_levels(config)

    if not config['local']['media_dir']:
        logging.warning('Config value local/media_dir is not set.')
        return

    if not config['local']['scan_timeout']:
        logging.warning('Config value local/scan_timeout is not set.')
        return

    # TODO: missing config error checking and other default setup code.

    updaters = {}
    for e in extensions:
        for updater_class in e.get_library_updaters():
            if updater_class and 'local' in updater_class.uri_schemes:
                updaters[e.ext_name] = updater_class

    if not updaters:
        logging.error('No usable library updaters found.')
        return
    elif len(updaters) > 1:
        logging.error('More than one library updater found. '
                      'Provided by: %s', ', '.join(updaters.keys()))
        return

    local_updater = updaters.values()[0](config)  # TODO: switch to actor?

    media_dir = config['local']['media_dir']

    uris_library = set()
    uris_update = set()
    uris_remove = set()

    logging.info('Checking tracks from library.')
    for track in local_updater.load():
        try:
            stat = os.stat(path.uri_to_path(track.uri))
            if int(stat.st_mtime) > track.last_modified:
                uris_update.add(track.uri)
            uris_library.add(track.uri)
        except OSError:
            uris_remove.add(track.uri)

    logging.info('Removing %d moved or deleted tracks.', len(uris_remove))
    for uri in uris_remove:
        local_updater.remove(uri)

    logging.info('Checking %s for new or modified tracks.', media_dir)
    for uri in path.find_uris(config['local']['media_dir']):
        if uri not in uris_library:
            uris_update.add(uri)

    logging.info('Found %d new or modified tracks.', len(uris_update))

    def store(data):
        track = translator(data)
        local_updater.add(track)
        logging.debug('Added %s', track.uri)

    def debug(uri, error, debug):
        logging.warning('Failed %s: %s', uri, error)
        logging.debug('Debug info for %s: %s', uri, debug)

    scan_timeout = config['local']['scan_timeout']

    logging.info('Scanning new and modified tracks.')
    # TODO: just pass the library in instead?
    scanner = Scanner(uris_update, store, debug, scan_timeout)
    try:
        scanner.start()
    except KeyboardInterrupt:
        scanner.stop()
        raise

    logging.info('Done scanning; commiting changes.')
    local_updater.commit()
Exemple #30
0
 def test_no_extensions(self, iter_entry_points_mock):
     iter_entry_points_mock.return_value = []
     assert ext.load_extensions() == []