discover/broadcast server location via mdns
This commit is contained in:
parent
4781c35643
commit
fabbcb669e
5 changed files with 148 additions and 10 deletions
4
debian/control
vendored
4
debian/control
vendored
|
@ -3,13 +3,13 @@ Maintainer: Jan Gerber <j@mailb.org>
|
|||
Section: python
|
||||
Priority: optional
|
||||
Build-Depends: python3-all, debhelper (>= 7.4.3), dh-python, python3-setuptools (>= 0.6b3),
|
||||
python3-ox, python3-six, python3-requests, python3-twisted
|
||||
python3-ox, python3-six, python3-requests, python3-twisted, python3-zeroconf, python3-netifaces
|
||||
Standards-Version: 3.9.1
|
||||
|
||||
Package: pandora-client
|
||||
Architecture: all
|
||||
Depends: python3, ${misc:Depends}, ${python3:Depends}, ffmpeg2theora, ffmpeg | libav-tools,
|
||||
python3-ox, python3-six, python3-requests, python3-twisted
|
||||
python3-ox, python3-six, python3-requests, python3-twisted, python3-zeroconf, python3-netifaces
|
||||
Description: pandora_client is a commandline client for pan.do/ra.
|
||||
You can use it to import videos into a pan.do/ra system.
|
||||
|
||||
|
|
|
@ -987,15 +987,33 @@ class Client(object):
|
|||
server.run(self, args)
|
||||
|
||||
def client(self, args):
|
||||
if not args:
|
||||
print('usage: %s client <server_url>\n\ti.e. %s client http://192.168.1.1:8789' % (sys.argv[0], sys.argv[0]))
|
||||
sys.exit(1)
|
||||
from . import client
|
||||
url = args[0]
|
||||
if len(args) == 1:
|
||||
urls = [u for u in args if u.startswith('http:')]
|
||||
name = [u for u in args if u not in urls]
|
||||
if not name:
|
||||
name = socket.gethostname()
|
||||
else:
|
||||
name = args[1]
|
||||
name = name[0]
|
||||
if not urls:
|
||||
from . import localnode
|
||||
nodes = localnode.LocalNodes()
|
||||
time.sleep(1)
|
||||
found = len(nodes)
|
||||
if not found:
|
||||
print('usage: %s client <server_url>\n\ti.e. %s client http://192.168.1.1:8789' % (sys.argv[0], sys.argv[0]))
|
||||
sys.exit(1)
|
||||
elif found > 1:
|
||||
print('found multiple servers, please select one, your options are:')
|
||||
for id, url in nodes.items():
|
||||
print('\t%s client %s' % (sys.argv[0], url))
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
for id, url in nodes.items():
|
||||
break
|
||||
print('connecting to %s (%s)' % (id, url))
|
||||
else:
|
||||
url = urls[0]
|
||||
from . import client
|
||||
c = client.DistributedClient(url, name)
|
||||
c.run()
|
||||
|
||||
|
|
107
pandora_client/localnode.py
Normal file
107
pandora_client/localnode.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
import socket
|
||||
import netifaces
|
||||
from zeroconf import (
|
||||
ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf
|
||||
)
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
service_type = '_pandoraclient._tcp.local.'
|
||||
|
||||
def get_broadcast_interfaces():
|
||||
return list(set(
|
||||
addr['addr']
|
||||
for iface in netifaces.interfaces()
|
||||
for addr in netifaces.ifaddresses(iface).get(socket.AF_INET, [])
|
||||
if addr.get('netmask') != '255.255.255.255' and addr.get('broadcast')
|
||||
))
|
||||
|
||||
|
||||
class Server(object):
|
||||
local_info = None
|
||||
local_ips = None
|
||||
|
||||
def __init__(self, port):
|
||||
self.port = port
|
||||
self.name = socket.gethostname().partition('.')[0] + '-%s' % port
|
||||
self.local_ips = get_broadcast_interfaces()
|
||||
self.zeroconf = {ip: Zeroconf(interfaces=[ip]) for ip in self.local_ips}
|
||||
self.register_service()
|
||||
|
||||
def register_service(self):
|
||||
if self.local_info:
|
||||
for local_ip, local_info in self.local_info:
|
||||
self.zeroconf[local_ip].unregister_service(local_info)
|
||||
self.local_info = None
|
||||
|
||||
local_name = socket.gethostname().partition('.')[0] + '.local.'
|
||||
port = self.port
|
||||
desc = {}
|
||||
self.local_info = []
|
||||
for i, local_ip in enumerate(get_broadcast_interfaces()):
|
||||
if i:
|
||||
name = '%s-%s.%s' % (self.name, i+1, service_type)
|
||||
else:
|
||||
name = '%s.%s' % (self.name, service_type)
|
||||
local_info = ServiceInfo(service_type, name,
|
||||
socket.inet_aton(local_ip), port, 0, 0, desc, local_name)
|
||||
self.zeroconf[local_ip].register_service(local_info)
|
||||
self.local_info.append((local_ip, local_info))
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
if self.local_info:
|
||||
for local_ip, local_info in self.local_info:
|
||||
try:
|
||||
self.zeroconf[local_ip].unregister_service(local_info)
|
||||
except:
|
||||
logger.debug('exception closing zeroconf', exc_info=True)
|
||||
self.local_info = None
|
||||
if self.zeroconf:
|
||||
for local_ip in self.zeroconf:
|
||||
try:
|
||||
self.zeroconf[local_ip].close()
|
||||
except:
|
||||
logger.debug('exception closing zeroconf', exc_info=True)
|
||||
self.zeroconf = None
|
||||
|
||||
class LocalNodes(dict):
|
||||
|
||||
def __init__(self):
|
||||
self.local_ips = get_broadcast_interfaces()
|
||||
self.zeroconf = {ip: Zeroconf(interfaces=[ip]) for ip in self.local_ips}
|
||||
self.browse()
|
||||
|
||||
def browse(self):
|
||||
self.browser = {
|
||||
ip: ServiceBrowser(self.zeroconf[ip], service_type, handlers=[self.on_service_state_change])
|
||||
for ip in self.zeroconf
|
||||
}
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
if self.zeroconf:
|
||||
for local_ip in self.zeroconf:
|
||||
try:
|
||||
self.zeroconf[local_ip].close()
|
||||
except:
|
||||
logger.debug('exception closing zeroconf', exc_info=True)
|
||||
self.zeroconf = None
|
||||
for id in list(self):
|
||||
self.pop(id, None)
|
||||
|
||||
def on_service_state_change(self, zeroconf, service_type, name, state_change):
|
||||
id = name.split('.')[0].split('-')[0]
|
||||
if state_change is ServiceStateChange.Added:
|
||||
info = zeroconf.get_service_info(service_type, name)
|
||||
if info:
|
||||
self[id] = 'http://%s:%s' % (socket.inet_ntoa(info.address), info.port)
|
||||
elif state_change is ServiceStateChange.Removed:
|
||||
logger.debug('remove: %s', id)
|
||||
self.pop(id, None)
|
||||
|
|
@ -205,7 +205,18 @@ def run(client, args=None):
|
|||
port = int(port)
|
||||
else:
|
||||
port = int(args[0])
|
||||
local = None
|
||||
else:
|
||||
try:
|
||||
from . import localnode
|
||||
local = localnode.Server(port)
|
||||
except:
|
||||
local = None
|
||||
reactor.listenTCP(port, site, interface=interface)
|
||||
print('listening on http://%s:%s' % (interface, port))
|
||||
if local:
|
||||
print('broadcasting location via mdns (you can just use pandora_client client on the other side)')
|
||||
client.update_encodes()
|
||||
reactor.run()
|
||||
if local:
|
||||
del local
|
||||
|
|
4
setup.py
4
setup.py
|
@ -52,7 +52,9 @@ setup(
|
|||
install_requires=[
|
||||
'ox >= 2.1.541,<3',
|
||||
'six',
|
||||
'requests >= 1.1.0'
|
||||
'requests >= 1.1.0',
|
||||
'zeroconf',
|
||||
'netifaces',
|
||||
],
|
||||
keywords=[],
|
||||
classifiers=[
|
||||
|
|
Loading…
Reference in a new issue