6 from threading
import Timer
7 from urllib
import quote
12 from plugin
import GetPlugin
14 SHARE_TEMPLATE
= '/TiVoConnect?Command=QueryContainer&Container=%s'
15 PLATFORM_MAIN
= 'pyTivo'
16 PLATFORM_VIDEO
= 'pc/pyTivo' # For the nice icon
19 def __init__(self
, names
):
22 def removeService(self
, server
, type, name
):
23 if name
in self
.names
:
24 self
.names
.remove(name
)
26 def addService(self
, server
, type, name
):
27 self
.names
.append(name
)
30 def __init__(self
, logger
):
31 """ Announce our shares via Zeroconf. """
35 self
.rz
= Zeroconf
.Zeroconf()
36 address
= inet_aton(config
.get_ip())
37 port
= int(config
.getPort())
38 for section
, settings
in config
.getShares():
39 ct
= GetPlugin(settings
['type']).CONTENT_TYPE
40 if ct
.startswith('x-container/'):
42 platform
= PLATFORM_VIDEO
44 platform
= PLATFORM_MAIN
45 logger
.info('Registering: %s' % section
)
46 self
.share_names
.append(section
)
47 desc
= {'path': SHARE_TEMPLATE
% quote(section
),
48 'platform': platform
, 'protocol': 'http'}
50 info
= Zeroconf
.ServiceInfo('_%s._tcp.local.' % tt
,
51 '%s._%s._tcp.local.' % (section
, tt
),
52 address
, port
, 0, 0, desc
)
53 self
.rz
.registerService(info
)
54 self
.share_info
.append(info
)
57 """ Look for TiVos using Zeroconf. """
58 VIDS
= '_tivo-videos._tcp.local.'
61 # Get the names of servers offering TiVo videos
62 browser
= Zeroconf
.ServiceBrowser(self
.rz
, VIDS
, ZCListener(names
))
64 # Give them half a second to respond
67 # Now get the addresses -- this is the slow part
69 info
= self
.rz
.getServiceInfo(VIDS
, name
)
70 if info
and 'TSN' in info
.properties
:
71 tsn
= info
.properties
['TSN']
72 address
= inet_ntoa(info
.getAddress())
73 config
.tivos
[tsn
] = address
74 name
= name
.replace('.' + VIDS
, '')
75 self
.logger
.info(name
)
76 config
.tivo_names
[tsn
] = name
79 self
.logger
.info('Unregistering: %s' % ' '.join(self
.share_names
))
80 for info
in self
.share_info
:
81 self
.rz
.unregisterService(info
)
86 self
.UDPSock
= socket(AF_INET
, SOCK_DGRAM
)
87 self
.UDPSock
.setsockopt(SOL_SOCKET
, SO_BROADCAST
, 1)
90 self
.platform
= PLATFORM_VIDEO
91 for section
, settings
in config
.getShares():
92 ct
= GetPlugin(settings
['type']).CONTENT_TYPE
93 if ct
.startswith('x-container/'):
94 if 'music' in ct
or 'photo' in ct
:
95 self
.platform
= PLATFORM_MAIN
99 logger
= logging
.getLogger('pyTivo.beacon')
101 logger
.info('Announcing shares...')
102 self
.bd
= ZCBroadcast(logger
)
104 logger
.error('Zeroconf failure')
107 logger
.info('Scanning for TiVos...')
112 def add_service(self
, service
):
113 self
.services
.append(service
)
116 def format_services(self
):
117 return ';'.join(self
.services
)
119 def format_beacon(self
, conntype
, services
=True):
120 beacon
= ['tivoconnect=1',
121 'method=%s' % conntype
,
122 'identity=%s' % config
.getGUID(),
123 'machine=%s' % gethostname(),
124 'platform=%s' % self
.platform
]
127 beacon
.append('services=' + self
.format_services())
129 beacon
.append('services=TiVoMediaServer:0/http')
131 return '\n'.join(beacon
)
133 def send_beacon(self
):
134 beacon_ips
= config
.getBeaconAddresses()
135 for beacon_ip
in beacon_ips
.split():
136 if beacon_ip
!= 'listen':
138 self
.UDPSock
.sendto(self
.format_beacon('broadcast'),
145 self
.timer
= Timer(60, self
.start
)
154 """ For the direct-connect, TCP-style beacon """
158 TCPSock
= socket(AF_INET
, SOCK_STREAM
)
159 TCPSock
.bind(('', 2190))
163 # Wait for a connection
164 client
, address
= TCPSock
.accept()
166 # Accept the client's beacon
167 client_length
= struct
.unpack('!I', client
.recv(4))[0]
168 client_message
= client
.recv(client_length
)
171 message
= self
.format_beacon('connected')
172 client
.send(struct
.pack('!I', len(message
)))
176 thread
.start_new_thread(server
, ())
178 def get_name(self
, address
):
179 """ Exchange beacons, and extract the machine name. """
180 our_beacon
= self
.format_beacon('connected', False)
181 machine_name
= re
.compile('machine=(.*)\n').search
185 tsock
.connect((address
, 2190))
187 tsock
.send(struct
.pack('!I', len(our_beacon
)))
188 tsock
.send(our_beacon
)
190 length
= struct
.unpack('!I', tsock
.recv(4))[0]
191 tivo_beacon
= tsock
.recv(length
)
195 name
= machine_name(tivo_beacon
).groups()[0]