diff --git a/mopidy/frontends/http/__init__.py b/mopidy/frontends/http/__init__.py index 6d84b25b..64cb88f9 100644 --- a/mopidy/frontends/http/__init__.py +++ b/mopidy/frontends/http/__init__.py @@ -21,6 +21,7 @@ class Extension(ext.Extension): schema['hostname'] = config.Hostname() schema['port'] = config.Port() schema['static_dir'] = config.Path(optional=True) + schema['zeroconf'] = config.String(optional=True) return schema def validate_environment(self): diff --git a/mopidy/frontends/http/actor.py b/mopidy/frontends/http/actor.py index 5e49d2cd..4e3493d4 100644 --- a/mopidy/frontends/http/actor.py +++ b/mopidy/frontends/http/actor.py @@ -11,6 +11,7 @@ from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool from mopidy import models from mopidy.core import CoreListener +from mopidy.utils import zeroconf from . import ws @@ -22,6 +23,12 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener): super(HttpFrontend, self).__init__() self.config = config self.core = core + + self.hostname = config['http']['hostname'] + self.port = config['http']['port'] + self.zeroconf_name = config['http']['zeroconf'] + self.zeroconf_service = None + self._setup_server() self._setup_websocket_plugin() app = self._create_app() @@ -30,8 +37,8 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener): def _setup_server(self): cherrypy.config.update({ 'engine.autoreload_on': False, - 'server.socket_host': self.config['http']['hostname'], - 'server.socket_port': self.config['http']['port'], + 'server.socket_host': self.hostname, + 'server.socket_port': self.port, }) def _setup_websocket_plugin(self): @@ -88,7 +95,21 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener): cherrypy.engine.start() logger.info('HTTP server running at %s', cherrypy.server.base()) + if self.zeroconf_name: + self.zeroconf_service = zeroconf.Zeroconf( + stype='_http._tcp', name=self.zeroconf_name, + host=self.hostname, port=self.port) + + if self.zeroconf_service.publish(): + logger.info('Registered HTTP with Zeroconf as "%s"', + self.zeroconf_service.name) + else: + logger.warning('Registering HTTP with Zeroconf failed.') + def on_stop(self): + if self.zeroconf_service: + self.zeroconf_service.unpublish() + logger.debug('Stopping HTTP server') cherrypy.engine.exit() logger.info('Stopped HTTP server') diff --git a/mopidy/frontends/http/ext.conf b/mopidy/frontends/http/ext.conf index 04fb1aae..fc239230 100644 --- a/mopidy/frontends/http/ext.conf +++ b/mopidy/frontends/http/ext.conf @@ -3,6 +3,7 @@ enabled = true hostname = 127.0.0.1 port = 6680 static_dir = +zeroconf = Mopidy HTTP server on $hostname [loglevels] cherrypy = warning diff --git a/mopidy/frontends/mpd/__init__.py b/mopidy/frontends/mpd/__init__.py index 276be450..571d6455 100644 --- a/mopidy/frontends/mpd/__init__.py +++ b/mopidy/frontends/mpd/__init__.py @@ -23,6 +23,7 @@ class Extension(ext.Extension): schema['password'] = config.Secret(optional=True) schema['max_connections'] = config.Integer(minimum=1) schema['connection_timeout'] = config.Integer(minimum=1) + schema['zeroconf'] = config.String(optional=True) return schema def validate_environment(self): diff --git a/mopidy/frontends/mpd/actor.py b/mopidy/frontends/mpd/actor.py index 4d983b73..9df7ba07 100644 --- a/mopidy/frontends/mpd/actor.py +++ b/mopidy/frontends/mpd/actor.py @@ -7,7 +7,7 @@ import pykka from mopidy.core import CoreListener from mopidy.frontends.mpd import session -from mopidy.utils import encoding, network, process +from mopidy.utils import encoding, network, process, zeroconf logger = logging.getLogger('mopidy.frontends.mpd') @@ -15,12 +15,16 @@ logger = logging.getLogger('mopidy.frontends.mpd') class MpdFrontend(pykka.ThreadingActor, CoreListener): def __init__(self, config, core): super(MpdFrontend, self).__init__() + hostname = network.format_hostname(config['mpd']['hostname']) - port = config['mpd']['port'] + self.hostname = hostname + self.port = config['mpd']['port'] + self.zeroconf_name = config['mpd']['zeroconf'] + self.zeroconf_service = None try: network.Server( - hostname, port, + self.hostname, self.port, protocol=session.MpdSession, protocol_kwargs={ 'config': config, @@ -34,9 +38,24 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener): encoding.locale_decode(error)) sys.exit(1) - logger.info('MPD server running at [%s]:%s', hostname, port) + logger.info('MPD server running at [%s]:%s', self.hostname, self.port) + + def on_start(self): + if self.zeroconf_name: + self.zeroconf_service = zeroconf.Zeroconf( + stype='_mpd._tcp', name=self.zeroconf_name, + host=self.hostname, port=self.port) + + if self.zeroconf_service.publish(): + logger.info('Registered MPD with Zeroconf as "%s"', + self.zeroconf_service.name) + else: + logger.warning('Registering MPD with Zeroconf failed.') def on_stop(self): + if self.zeroconf_service: + self.zeroconf_service.unpublish() + process.stop_actors_by_class(session.MpdSession) def send_idle(self, subsystem): diff --git a/mopidy/frontends/mpd/ext.conf b/mopidy/frontends/mpd/ext.conf index bf77100c..c62c37ef 100644 --- a/mopidy/frontends/mpd/ext.conf +++ b/mopidy/frontends/mpd/ext.conf @@ -5,3 +5,4 @@ port = 6600 password = max_connections = 20 connection_timeout = 60 +zeroconf = Mopidy MPD server on $hostname diff --git a/mopidy/utils/zeroconf.py b/mopidy/utils/zeroconf.py new file mode 100644 index 00000000..c1781867 --- /dev/null +++ b/mopidy/utils/zeroconf.py @@ -0,0 +1,81 @@ +from __future__ import unicode_literals + +import logging +import re +import socket +import string + +logger = logging.getLogger('mopidy.utils.zerconf') + +try: + import dbus +except ImportError: + dbus = None + +_AVAHI_IF_UNSPEC = -1 +_AVAHI_PROTO_UNSPEC = -1 +_AVAHI_PUBLISHFLAGS_NONE = 0 + + +def _filter_loopback_and_meta_addresses(host): + # TODO: see if we can find a cleaner way of handling this. + if re.search(r'(?