Better handling of scroll bars. More to do here.
[pyTivo/TheBayer.git] / mind.py
blob3d83cfa9d52b2be17600177748aeb65e89f1b02b
1 import cookielib
2 import logging
3 import time
4 import urllib2
5 import urllib
6 import warnings
8 import config
10 try:
11 import xml.etree.ElementTree as ElementTree
12 except ImportError:
13 try:
14 import elementtree.ElementTree as ElementTree
15 except ImportError:
16 warnings.warn('Python 2.5 or higher or elementtree is ' +
17 'needed to use the TivoPush')
19 if 'ElementTree' not in locals():
21 class Mind:
22 def __init__(self, *arg, **karg):
23 raise Exception('Python 2.5 or higher or elementtree is ' +
24 'needed to use the TivoPush')
26 else:
28 class Mind:
29 def __init__(self, username, password):
30 self.__logger = logging.getLogger('pyTivo.mind')
31 self.__username = username
32 self.__password = password
34 cj = cookielib.CookieJar()
35 cp = urllib2.HTTPCookieProcessor(cj)
36 self.__opener = urllib2.build_opener(cp)
38 self.__login()
40 if not self.__pcBodySearch():
41 self.__pcBodyStore('pyTivo', True)
43 def pushVideo(self, tsn, url, description, duration, size,
44 title, subtitle, source='', mime=''):
45 # It looks like tivo only supports one pc per house
46 pc_body_id = self.__pcBodySearch()[0]
48 if not source:
49 source = title
51 data = {
52 'bodyId': 'tsn:' + tsn,
53 'description': description,
54 'duration': duration,
55 'partnerId': 'tivo:pt.3187',
56 'pcBodyId': pc_body_id,
57 'publishDate': time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
58 'size': size,
59 'source': source,
60 'state': 'complete',
61 'title': title
64 if mime == 'video/mp4':
65 data['encodingType'] = 'avcL41MP4'
66 data['url'] = url + '?Format=' + mime
67 elif mime == 'video/bif':
68 data['encodingType'] = 'vc1ApL3'
69 data['url'] = url + '?Format=' + mime
70 else:
71 data['encodingType'] = 'mpeg2ProgramStream'
72 data['url'] = url
74 if subtitle:
75 data['subtitle'] = subtitle
77 offer_id, content_id = self.__bodyOfferModify(data)
78 self.__subscribe(offer_id, content_id, tsn)
80 def getDownloadRequests(self):
81 NEEDED_VALUES = [
82 'bodyId',
83 'bodyOfferId',
84 'description',
85 'partnerId',
86 'pcBodyId',
87 'publishDate',
88 'source',
89 'state',
90 'subscriptionId',
91 'subtitle',
92 'title',
93 'url'
96 # It looks like tivo only supports one pc per house
97 pc_body_id = self.__pcBodySearch()[0]
99 requests = []
100 offer_list = self.__bodyOfferSchedule(pc_body_id)
102 for offer in offer_list.findall('bodyOffer'):
103 d = {}
104 if offer.findtext('state') != 'scheduled':
105 continue
107 for n in NEEDED_VALUES:
108 d[n] = offer.findtext(n)
109 requests.append(d)
111 return requests
113 def completeDownloadRequest(self, request, mime=''):
114 if mime == 'video/mp4':
115 request['encodingType'] = 'avcL41MP4'
116 request['url'] += '?Format=' + mime
117 elif mime == 'video/bif':
118 request['encodingType'] = 'vc1ApL3'
119 request['url'] = url + '?Format=' + mime
120 else:
121 request['encodingType'] = 'mpeg2ProgramStream'
122 request['state'] = 'complete'
123 request['type'] = 'bodyOfferModify'
124 request['updateDate'] = time.strftime('%Y-%m-%d %H:%M%S',
125 time.gmtime())
127 offer_id, content_id = self.__bodyOfferModify(request)
128 self.__subscribe(offer_id, content_id, request['bodyId'][4:])
130 def getXMPPLoginInfo(self):
131 # It looks like tivo only supports one pc per house
132 pc_body_id = self.__pcBodySearch()[0]
134 xml = self.__bodyXmppInfoGet(pc_body_id)
136 results = {
137 'server': xml.findtext('server'),
138 'port': int(xml.findtext('port')),
139 'username': xml.findtext('xmppId')
142 for sendPresence in xml.findall('sendPresence'):
143 results.setdefault('presence_list',[]).append(sendPresence.text)
145 return results
147 def __login(self):
149 data = {
150 'cams_security_domain': 'tivocom',
151 'cams_login_config': 'http',
152 'cams_cb_username': self.__username,
153 'cams_cb_password': self.__password,
154 'cams_original_url': '/mind/mind7?type=infoGet'
157 r = urllib2.Request(
158 'https://mind.tivo.com:8181/mind/login',
159 urllib.urlencode(data)
161 try:
162 result = self.__opener.open(r)
163 except:
164 pass
166 self.__logger.debug('__login\n%s' % (data))
168 def __dict_request(self, data, req):
169 r = urllib2.Request(
170 'https://mind.tivo.com:8181/mind/mind7?type=' + req,
171 dictcode(data),
172 {'Content-Type': 'x-tivo/dict-binary'}
174 result = self.__opener.open(r)
176 xml = ElementTree.parse(result).find('.')
178 self.__logger.debug('%s\n%s\n\n%sg' % (req, data,
179 ElementTree.tostring(xml)))
180 return xml
182 def __bodyOfferModify(self, data):
183 """Create an offer"""
185 xml = self.__dict_request(data, 'bodyOfferModify&bodyId=' +
186 data['bodyId'])
188 if xml.findtext('state') != 'complete':
189 raise Exception(ElementTree.tostring(xml))
191 offer_id = xml.findtext('offerId')
192 content_id = offer_id.replace('of','ct')
194 return offer_id, content_id
197 def __subscribe(self, offer_id, content_id, tsn):
198 """Push the offer to the tivo"""
199 data = {
200 'bodyId': 'tsn:' + tsn,
201 'idSetSource': {
202 'contentId': content_id,
203 'offerId': offer_id,
204 'type': 'singleOfferSource'
206 'title': 'pcBodySubscription',
207 'uiType': 'cds'
210 return self.__dict_request(data, 'subscribe&bodyId=tsn:' + tsn)
212 def __bodyOfferSchedule(self, pc_body_id):
213 """Get pending stuff for this pc"""
215 data = {'pcBodyId': pc_body_id}
216 return self.__dict_request(data, 'bodyOfferSchedule')
218 def __pcBodySearch(self):
219 """Find PCS"""
221 xml = self.__dict_request({}, 'pcBodySearch')
223 return [id.text for id in xml.findall('pcBody/pcBodyId')]
225 def __collectionIdSearch(self, url):
226 """Find collection ids"""
228 xml = self.__dict_request({'url': url}, 'collectionIdSearch')
229 return xml.findtext('collectionId')
231 def __pcBodyStore(self, name, replace=False):
232 """Setup a new PC"""
234 data = {
235 'name': name,
236 'replaceExisting': str(replace).lower()
239 return self.__dict_request(data, 'pcBodyStore')
241 def __bodyXmppInfoGet(self, body_id):
243 return self.__dict_request({'bodyId': body_id},
244 'bodyXmppInfoGet&bodyId=' + body_id)
247 def dictcode(d):
248 """Helper to create x-tivo/dict-binary"""
249 output = []
251 keys = [str(k) for k in d]
252 keys.sort()
254 for k in keys:
255 v = d[k]
257 output.append( varint( len(k) ) )
258 output.append( k )
260 if isinstance(v, dict):
261 output.append( chr(2) )
262 output.append( dictcode(v) )
264 else:
265 v = unicode(v).encode('utf-8')
266 output.append( chr(1) )
267 output.append( varint( len(v) ) )
268 output.append( v )
270 output.append( chr(0) )
272 output.append( chr(0x80) )
274 return ''.join(output)
276 def varint(i):
277 output = []
278 while i > 0x7f:
279 output.append( chr(i & 0x7f) )
280 i >>= 7
281 output.append( chr(i | 0x80) )
282 return ''.join(output)
284 def getMind(tsn=None):
285 username = config.getTivoUsername(tsn)
286 password = config.getTivoPassword(tsn)
288 if not username or not password:
289 raise Exception("tivo_username and tivo_password required")
291 return Mind(username, password)