Beispiel #1
0
        def _worker(host, port):
            try:
                if exclude_local and (host == 'localhost'
                                      or host == Config.get('device_id')):
                    return

                server_status = self.status(host=host, port=port).output
                client_status = self.status(host=host, port=port,
                                            client=Config.get('device_id')).output

                if client_status.get('config', {}).get('volume', {}).get('muted'):
                    return

                group = [g for g in server_status.get('groups', {})
                         if g.get('id') == client_status.get('group_id')].pop(0)

                if group.get('muted'):
                    return

                stream = [s for s in server_status.get('streams')
                          if s.get('id') == group.get('stream_id')].pop(0)

                if stream.get('status') != 'playing':
                    return

                playing_hosts[host] = port
            except Exception as e:
                self.logger.warning(('Error while retrieving the status of ' +
                                     'Snapcast host at {}:{}: {}').format(
                    host, port, str(e)))
Beispiel #2
0
def plugin_route(plugin):
    """ Route to the plugin pane template """
    js_folder = os.path.abspath(os.path.join(template_folder, '..', 'static', 'js'))
    style_folder = os.path.abspath(os.path.join(template_folder, '..', 'static', 'css', 'dist'))
    template_file = os.path.join(template_folder, 'plugins', plugin, 'index.html')
    script_file = os.path.join(js_folder, 'plugins', plugin, 'index.js')
    style_file = os.path.join(style_folder, 'webpanel', 'plugins', plugin+'.css')

    conf = Config.get(plugin) or {}
    if os.path.isfile(template_file):
        conf['_template_file'] = '/' + '/'.join(template_file.split(os.sep)[-3:])

    if os.path.isfile(script_file):
        conf['_script_file'] = '/'.join(script_file.split(os.sep)[-4:])

    if os.path.isfile(style_file):
        conf['_style_file'] = 'css/dist/' + style_file[len(style_folder)+1:]

    http_conf = Config.get('backend.http')
    return render_template('plugin.html',
                           plugin=plugin,
                           conf=conf,
                           template=conf.get('_template_file', {}),
                           script=conf.get('_script_file', {}),
                           style=conf.get('_style_file', {}),
                           utils=HttpUtils,
                           token=Config.get('token'),
                           websocket_port=get_websocket_port(),
                           template_folder=template_folder,
                           static_folder=static_folder,
                           has_ssl=http_conf.get('ssl_cert') is not None)
Beispiel #3
0
def build(args):
    global workdir

    ports = set()
    parser = argparse.ArgumentParser(
        prog='platydock build',
        description='Build a Platypush image from a config.yaml')

    parser.add_argument('-c',
                        '--config',
                        type=str,
                        required=True,
                        help='Path to the platypush configuration file')
    parser.add_argument('-p',
                        '--python-version',
                        type=str,
                        default='3.9',
                        help='Python version to be used')

    opts, args = parser.parse_known_args(args)

    cfgfile = os.path.abspath(os.path.expanduser(opts.config))
    manifest._available_package_manager = 'apt'  # Force apt for Debian-based Docker images
    install_cmds = manifest.get_dependencies_from_conf(cfgfile)
    python_version = opts.python_version
    backend_config = Config.get_backends()

    # Container exposed ports
    if backend_config.get('http'):
        from platypush.backend.http import HttpBackend
        # noinspection PyProtectedMember
        ports.add(backend_config['http'].get('port',
                                             HttpBackend._DEFAULT_HTTP_PORT))
        # noinspection PyProtectedMember
        ports.add(backend_config['http'].get(
            'websocket_port', HttpBackend._DEFAULT_WEBSOCKET_PORT))

    if backend_config.get('tcp'):
        ports.add(backend_config['tcp']['port'])

    if backend_config.get('websocket'):
        from platypush.backend.websocket import WebsocketBackend
        # noinspection PyProtectedMember
        ports.add(backend_config['websocket'].get(
            'port', WebsocketBackend._default_websocket_port))

    dev_dir = os.path.join(workdir, Config.get('device_id'))
    generate_dockerfile(deps=dict(install_cmds),
                        ports=ports,
                        cfgfile=cfgfile,
                        device_dir=dev_dir,
                        python_version=python_version)

    subprocess.call([
        'docker', 'build', '-t',
        'platypush-{}'.format(Config.get('device_id')), dev_dir
    ])
Beispiel #4
0
def dashboard():
    """ Route for the fullscreen dashboard """
    http_conf = Config.get('backend.http')
    dashboard_conf = http_conf.get('dashboard', {})

    return render_template('dashboard.html',
                           config=dashboard_conf,
                           utils=HttpUtils,
                           token=Config.get('token'),
                           static_folder=static_folder,
                           template_folder=template_folder,
                           websocket_port=get_websocket_port(),
                           has_ssl=http_conf.get('ssl_cert') is not None)
Beispiel #5
0
def build(args):
    global workdir

    ports = set()
    deps = set()

    parser = argparse.ArgumentParser(prog='platydock build',
                                     description='Build a Platypush image ' +
                                     'from a config.yaml')

    parser.add_argument('-c',
                        '--config',
                        type=str,
                        required=True,
                        help='Path to the platypush configuration file')

    opts, args = parser.parse_known_args(args)

    cfgfile = os.path.abspath(os.path.expanduser(opts.config))
    Config.init(cfgfile)
    register_backends()
    backend_config = Config.get_backends()

    if backend_config.get('http'):
        http_backend = get_backend('http')
        ports.add(http_backend.port)
        if http_backend.websocket_port:
            ports.add(http_backend.websocket_port)

    if backend_config.get('tcp'):
        ports.add(get_backend('tcp').port)

    if backend_config.get('websocket'):
        ports.add(get_backend('websocket').port)

    for name in Config.get_backends().keys():
        deps.update(_parse_deps(get_backend(name)))

    for name in Config.get_plugins().keys():
        try:
            deps.update(_parse_deps(get_plugin(name)))
        except:
            pass

    devdir = os.path.join(workdir, Config.get('device_id'))
    generate_dockerfile(deps=deps, ports=ports, cfgfile=cfgfile, devdir=devdir)

    subprocess.call([
        'docker', 'build', '-t',
        'platypush-{}'.format(Config.get('device_id')), devdir
    ])
Beispiel #6
0
    def __init__(self, url, title=None, headers=None, params=None, max_entries=None,
                 extract_content=False, digest_format=None, user_agent: str = user_agent,
                 body_style: str = 'font-size: 22px; ' +
                                   'font-family: "Merriweather", Georgia, "Times New Roman", Times, serif;',
                 title_style: str = 'margin-top: 30px',
                 subtitle_style: str = 'margin-top: 10px; page-break-after: always',
                 article_title_style: str = 'page-break-before: always',
                 article_link_style: str = 'color: #555; text-decoration: none; border-bottom: 1px dotted',
                 article_content_style: str = '', *argv, **kwargs):
        """
        :param url: URL to the RSS feed to be monitored.
        :param title: Optional title for the feed.
        :param headers: Extra headers to be passed to the request.
        :param params: Extra GET parameters to be appended to the URL.
        :param max_entries: Maximum number of entries that will be returned in a single
            :class:`platypush.message.event.http.rss.NewFeedEvent` event.
        :param extract_content: Whether the context should also be extracted (through the
            :class:`platypush.plugins.http.webpage.HttpWebpagePlugin` plugin) (default: ``False``).
        :param digest_format: Format of the digest output file (default: None, text. Other supported types: ``html``
            and ``pdf`` (requires the ``weasyprint`` module installed).
        :param user_agent: User agent string to be passed on the request.
        :param body_style: CSS style for the body.
        :param title_style: CSS style for the feed title.
        :param subtitle_style: CSS style for the feed subtitle.
        :param article_title_style: CSS style for the article titles.
        :param article_link_style: CSS style for the article link.
        :param article_content_style: CSS style for the article content.
        """
        self.workdir = os.path.join(os.path.expanduser(Config.get('workdir')), 'feeds')
        self.dbfile = os.path.join(self.workdir, 'rss.db')
        self.url = url
        self.title = title
        self.max_entries = max_entries
        self.user_agent = user_agent
        self.body_style = body_style
        self.title_style = title_style
        self.subtitle_style = subtitle_style
        self.article_title_style = article_title_style
        self.article_link_style = article_link_style
        self.article_content_style = article_content_style

        # If true, then the http.webpage plugin will be used to parse the content
        self.extract_content = extract_content

        self.digest_format = digest_format.lower() if digest_format else None  # Supported formats: html, pdf

        os.makedirs(os.path.expanduser(os.path.dirname(self.dbfile)), exist_ok=True)

        if headers is None:
            headers = {}
        headers['User-Agent'] = self.user_agent

        request_args = {
            'method': 'get',
            'url': self.url,
            'headers': headers,
            'params': params or {},
        }

        super().__init__(skip_first_call=False, args=request_args, *argv, **kwargs)
Beispiel #7
0
def resources_path(path):
    """ Custom static resources """
    path_tokens = path.split('/')
    filename = path_tokens.pop(-1)
    http_conf = Config.get('backend.http')
    resource_dirs = http_conf.get('resource_dirs', {})

    while path_tokens:
        if '/'.join(path_tokens) in resource_dirs:
            break
        path_tokens.pop()

    if not path_tokens:
        # Requested resource not found in the allowed resource_dirs
        abort(404)

    base_path = '/'.join(path_tokens)
    real_base_path = os.path.abspath(os.path.expanduser(resource_dirs[base_path]))
    real_path = real_base_path

    file_path = [s for s in re.sub(r'^{}(.*)$'.format(base_path), '\\1', path)
                 .split('/') if s]

    for p in file_path[:-1]:
        real_path += os.sep + p
        file_path.pop(0)

    file_path = file_path.pop(0)
    if not real_path.startswith(real_base_path):
        # Directory climbing attempt
        abort(404)

    return send_from_directory(real_path, file_path)
Beispiel #8
0
def get_or_create_ngrok_tunnel() -> str:
    """
    This method creates an ngrok tunnel for the local web application,
    useful to register public HTTPS callback URLs on the fly from plugins
    and backends.
    """
    global _app_tunnel_url
    with _app_tunnel_lock:
        if _app_tunnel_url:
            return _app_tunnel_url

        local_port = _get_http_port()

        ngrok = None
        if Config.get('ngrok'):
            ngrok = get_plugin('ngrok')

        assert ngrok, 'The ngrok plugin is required in order to subscribe to notifications'
        tunnel_response = ngrok.create_tunnel(
            resource=local_port,
            protocol='http',
            bind_tls=True,
        ).output

        _app_tunnel_url = tunnel_response.get('url')
        assert _app_tunnel_url, 'Unable to create an ngrok tunnel'
        return _app_tunnel_url
Beispiel #9
0
def _get_http_port() -> int:
    http = None
    if Config.get('backend.http'):
        http = get_backend('http')

    assert http, 'The http backend is required in order to subscribe to notifications'
    return http.port
Beispiel #10
0
    def __init__(self,
                 host: Optional[str] = None,
                 port: int = 8002,
                 timeout: Optional[int] = 5,
                 name='platypush',
                 token_file: Optional[str] = None,
                 **kwargs):
        """
        :param host: IP address or host name of the smart TV.
        :param port: Websocket port (default: 8002).
        :param timeout: Connection timeout in seconds (default: 5, specify 0 or None for no timeout).
        :param name: Name of the remote device (default: platypush).
        :param token_file: Path to the token file (default: ``~/.local/share/platypush/samsungtvws/token.txt``)
        """
        super().__init__(**kwargs)
        self.workdir = os.path.join(Config.get('workdir'), 'samsungtvws')
        if not token_file:
            token_file = os.path.join(self.workdir, 'token.txt')

        self.host = host
        self.port = port
        self.timeout = timeout
        self.name = name
        self.token_file = token_file
        self._connections: Dict[Tuple[host, port], SamsungTVWS] = {}
        os.makedirs(self.workdir, mode=0o700, exist_ok=True)
Beispiel #11
0
    def __init__(self, config_file=None, backend=None, on_response=None):
        """
        Constructor.
        Params:
            config_file -- Path to the configuration file - default:
                           ~/.config/platypush/config.yaml or
                           /etc/platypush/config.yaml)
            backend     -- Name of the backend where pusher will send the
                           request and wait for the response (kafka
                           or pushbullet). Default: whatever is specified
                           with pusher=true in your configuration file
            on_response -- Method that will be invoked upon response receipt.
                           Takes a platypush.message.response.Response as arg.
                           Default: print the response and exit.
        """

        # Initialize the configuration
        self.config_file = config_file
        log_conf = Config.get('logging')
        Config.init(config_file)
        logging.basicConfig(level=log_conf['level'] if log_conf
                            and 'level' in log_conf else logging.info,
                            stream=sys.stdout)

        self.on_response = on_response or self.default_on_response()
        self.backend = backend or Config.get_default_pusher_backend()
        self.bus = Bus()
Beispiel #12
0
 def get_switch_plugins(self) -> dict:
     """
     :return: The list of enabled switch plugins as a ``name -> configuration`` map.
     """
     from platypush.plugins.switch import SwitchPlugin
     return {
         name: Config.get(name)
         for name, plugin in get_enabled_plugins().items()
         if isinstance(plugin, SwitchPlugin)
     }
Beispiel #13
0
def get_credentials_filename(scope):
    from platypush.config import Config

    scope_name = scope.split('/')[-1]
    credentials_dir = os.path.join(Config.get('workdir'), 'credentials',
                                   'google')

    if not os.path.exists(credentials_dir):
        os.makedirs(credentials_dir)

    return os.path.join(credentials_dir, scope_name + '.json')