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
|
Section: python
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: python3-all, debhelper (>= 7.4.3), dh-python, python3-setuptools (>= 0.6b3),
|
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
|
Standards-Version: 3.9.1
|
||||||
|
|
||||||
Package: pandora-client
|
Package: pandora-client
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: python3, ${misc:Depends}, ${python3:Depends}, ffmpeg2theora, ffmpeg | libav-tools,
|
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.
|
Description: pandora_client is a commandline client for pan.do/ra.
|
||||||
You can use it to import videos into a pan.do/ra system.
|
You can use it to import videos into a pan.do/ra system.
|
||||||
|
|
||||||
|
|
|
@ -987,15 +987,33 @@ class Client(object):
|
||||||
server.run(self, args)
|
server.run(self, args)
|
||||||
|
|
||||||
def client(self, args):
|
def client(self, args):
|
||||||
if not args:
|
urls = [u for u in args if u.startswith('http:')]
|
||||||
print('usage: %s client <server_url>\n\ti.e. %s client http://192.168.1.1:8789' % (sys.argv[0], sys.argv[0]))
|
name = [u for u in args if u not in urls]
|
||||||
sys.exit(1)
|
if not name:
|
||||||
from . import client
|
|
||||||
url = args[0]
|
|
||||||
if len(args) == 1:
|
|
||||||
name = socket.gethostname()
|
name = socket.gethostname()
|
||||||
else:
|
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 = client.DistributedClient(url, name)
|
||||||
c.run()
|
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)
|
port = int(port)
|
||||||
else:
|
else:
|
||||||
port = int(args[0])
|
port = int(args[0])
|
||||||
|
local = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
from . import localnode
|
||||||
|
local = localnode.Server(port)
|
||||||
|
except:
|
||||||
|
local = None
|
||||||
reactor.listenTCP(port, site, interface=interface)
|
reactor.listenTCP(port, site, interface=interface)
|
||||||
print('listening on http://%s:%s' % (interface, port))
|
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()
|
client.update_encodes()
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
if local:
|
||||||
|
del local
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -52,7 +52,9 @@ setup(
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'ox >= 2.1.541,<3',
|
'ox >= 2.1.541,<3',
|
||||||
'six',
|
'six',
|
||||||
'requests >= 1.1.0'
|
'requests >= 1.1.0',
|
||||||
|
'zeroconf',
|
||||||
|
'netifaces',
|
||||||
],
|
],
|
||||||
keywords=[],
|
keywords=[],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
Loading…
Reference in a new issue