root/mind.py

Revision 0b5554584f8f91075dedc0252c2448e3322342c5, 10.6 kB (checked in by Jason Michalski <jmichalski@jmichalski-desktop.(none)>, 5 months ago)

Lets get the body id before using it.

  • Property mode set to 100644
Line 
1 import cookielib
2 import urllib2
3 import urllib
4 import struct
5 import httplib
6 import time
7 import warnings
8 import itertools
9 import config
10 import logging
11
12 try:
13     import xml.etree.ElementTree as ElementTree
14 except ImportError:
15     try:
16         import elementtree.ElementTree as ElementTree
17     except ImportError:
18         warnings.warn('Python 2.5 or higher or elementtree is needed to use the TivoPush')
19
20 if 'ElementTree' not in locals():
21
22     class Mind:
23         def __init__(self, *arg, **karg):
24             raise Exception('Python 2.5 or higher or elementtree is needed to use the TivoPush')
25
26 else:
27
28     class Mind:
29         def __init__(self, username, password):
30             self.__logger = logging.getLogger('pyTivo.mind')
31             self.__username = username
32             self.__password = password
33
34             self.__cj = cookielib.CookieJar()
35             self.__opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.__cj))
36
37             self.__login()
38
39             if not self.__pcBodySearch():
40                 self.__pcBodyStore('pyTivo', True)
41
42         def pushVideo(self, tsn, url, description, duration, size, title, subtitle):
43             # It looks like tivo only supports one pc per house
44             pc_body_id = self.__pcBodySearch()[0]
45
46             data = {
47                 'bodyId' : 'tsn:' + tsn,
48                 'description' : description,
49                 'duration' : duration,
50                 'encodingType' : 'mpeg2ProgramStream',
51                 'partnerId' : 'tivo:pt.3187',
52                 'pcBodyId' : pc_body_id,
53                 'publishDate' : time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
54                 'size' : size,
55                 'source' : 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
56                 'state' : 'complete',
57                 'subtitle' : subtitle,
58                 'title' : title,
59                 'url' : url,
60             }
61
62             offer_id, content_id = self.__bodyOfferModify(data)
63             self.__subscribe(offer_id, content_id, tsn)
64
65         def getDownloadRequests(self):
66             NEEDED_VALUES = [
67                 'bodyId',
68                 'bodyOfferId',
69                 'description',
70                 'partnerId',
71                 'pcBodyId',
72                 'publishDate',
73                 'source',
74                 'state',
75                 'subscriptionId',
76                 'subtitle',
77                 'title',
78                 'url',
79             ]
80
81             # It looks like tivo only supports one pc per house
82             pc_body_id = self.__pcBodySearch()[0]
83
84             requests = []
85             offer_list = self.__bodyOfferSchedule(pc_body_id)
86
87             for offer in offer_list.findall('bodyOffer'):
88                 d = {}
89                 if offer.findtext('state') != 'scheduled':
90                     continue
91
92                 for n in NEEDED_VALUES:
93                     d[n] = offer.findtext(n)
94                 requests.append(d)
95
96             return requests
97
98         def completeDownloadRequest(self, request):
99             request['encodingType'] = 'mpeg2ProgramStream'
100             request['state'] = 'complete'
101             request['type'] = 'bodyOfferModify'
102             request['updateDate'] = time.strftime('%Y-%m-%d %H:%M%S', time.gmtime())
103
104             offer_id, content_id = self.__bodyOfferModify(request)
105             self.__subscribe(offer_id, content_id, request['bodyId'][4:])
106
107
108         def getXMPPLoginInfo(self):
109             # It looks like tivo only supports one pc per house
110             pc_body_id = self.__pcBodySearch()[0]
111
112             xml = self.__bodyXmppInfoGet(pc_body_id)
113
114             results = {}
115             results['server'] = xml.findtext('server')
116             results['port'] = int(xml.findtext('port'))
117             results['username'] = xml.findtext('xmppId')
118
119             for sendPresence in xml.findall('sendPresence'):
120                 results.setdefault('presence_list', []).append(sendPresence.text)
121
122             return results
123
124         def __login(self):
125
126             data = {
127                 'cams_security_domain' : 'tivocom',
128                 'cams_login_config' : 'http',
129                 'cams_cb_username' : self.__username,
130                 'cams_cb_password' : self.__password,
131                 'cams_original_url' : '/mind/mind7?type=infoGet'
132             }
133
134             r =  urllib2.Request(
135                 'https://mind.tivo.com:8181/mind/login',
136                 urllib.urlencode(data)
137             )
138             try:
139                 result = self.__opener.open(r)
140             except:
141                 pass
142
143             self.__logger.debug('__login\n%s' % (data))
144
145         def __bodyOfferModify(self, data):
146             """Create an offer"""
147             r = urllib2.Request(
148                 'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferModify&bodyId=' + data['bodyId'],
149                 dictcode(data),
150                 {'Content-Type' : 'x-tivo/dict-binary'}
151             )
152             result = self.__opener.open(r)
153
154             xml = ElementTree.parse(result).find('.')
155
156             self.__logger.debug('__bodyOfferModify\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
157
158             if xml.findtext('state') != 'complete':
159                 raise Exception(ElementTree.tostring(xml))
160
161             offer_id = xml.findtext('offerId')
162             content_id = offer_id.replace('of','ct')
163
164             return offer_id, content_id
165
166
167         def __subscribe(self, offer_id, content_id, tsn):
168             """Push the offer to the tivo"""
169             data =  {
170                 'bodyId' : 'tsn:' + tsn,
171                 'idSetSource' : {
172                     'contentId': content_id,
173                     'offerId' : offer_id,
174                     'type' : 'singleOfferSource',
175                 },
176                 'title' : 'pcBodySubscription',
177                 'uiType' : 'cds',
178             }
179
180             r = urllib2.Request(
181                 'https://mind.tivo.com:8181/mind/mind7?type=subscribe&bodyId=tsn:' + tsn,
182                 dictcode(data),
183                 {'Content-Type' : 'x-tivo/dict-binary'}
184             )
185             result = self.__opener.open(r)
186
187             xml = ElementTree.parse(result).find('.')
188
189             self.__logger.debug('__subscribe\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
190
191             return xml
192
193         def __bodyOfferSchedule(self, pc_body_id):
194             """Get pending stuff for this pc"""
195
196             data = {'pcBodyId' : pc_body_id,}
197             r = urllib2.Request(
198                 'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferSchedule',
199                 dictcode(data),
200                 {'Content-Type' : 'x-tivo/dict-binary'}
201             )
202             result = self.__opener.open(r)
203
204             xml = ElementTree.parse(result).find('.')
205
206             self.__logger.debug('bodyOfferSchedule\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
207
208             return xml
209
210         def __pcBodySearch(self):
211             """Find PCS"""
212
213             data = {}
214             r = urllib2.Request(
215                 'https://mind.tivo.com:8181/mind/mind7?type=pcBodySearch',
216                 dictcode(data),
217                 {'Content-Type' : 'x-tivo/dict-binary'}
218             )
219             result = self.__opener.open(r)
220
221             xml = ElementTree.parse(result).find('.')
222
223
224             self.__logger.debug('__pcBodySearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
225
226             return [id.text for id in xml.findall('pcBody/pcBodyId')]
227
228         def __collectionIdSearch(self, url):
229             """Find collection ids"""
230
231             data = {'url' : url}
232             r = urllib2.Request(
233                 'https://mind.tivo.com:8181/mind/mind7?type=collectionIdSearch',
234                 dictcode(data),
235                 {'Content-Type' : 'x-tivo/dict-binary'}
236             )
237             result = self.__opener.open(r)
238
239             xml = ElementTree.parse( result ).find('.')
240             collection_id = xml.findtext('collectionId')
241
242             self.__logger.debug('__collectionIdSearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
243
244             return collection_id
245
246         def __pcBodyStore(self, name, replace=False):
247             """Setup a new PC"""
248
249             data = {
250                 'name' : name,
251                 'replaceExisting' : str(replace).lower(),
252             }
253
254             r = urllib2.Request(
255                 'https://mind.tivo.com:8181/mind/mind7?type=pcBodyStore',
256                 dictcode(data),
257                 {'Content-Type' : 'x-tivo/dict-binary'}
258             )
259             result = self.__opener.open(r)
260
261             xml = ElementTree.parse(result).find('.')
262
263             self.__logger.debug('__pcBodySearch\n%s\n\n%s' % (data, ElementTree.tostring(xml)))
264
265             return xml
266
267         def __bodyXmppInfoGet(self, body_id):
268
269             data = {
270                 'bodyId' : body_id,
271             }
272
273             r = urllib2.Request(
274                 'https://mind.tivo.com:8181/mind/mind7?type=bodyXmppInfoGet&bodyId=' + body_id,
275                 dictcode(data),
276                 {'Content-Type' : 'x-tivo/dict-binary'}
277             )
278
279             result = self.__opener.open(r)
280
281             xml = ElementTree.parse(result).find('.')
282
283             self.__logger.debug('__bodyXmppInfoGe\n%s\n\n%s' % (data, ElementTree.tostring(xml)))
284
285             return xml
286
287
288     def dictcode(d):
289         """Helper to create x-tivo/dict-binary"""
290         output = []
291
292         keys = [str(k) for k in d]
293         keys.sort()
294
295         for k in keys:
296             v = d[k]
297
298             output.append( varint( len(k) ) )
299             output.append( k )
300
301             if isinstance(v, dict):
302                 output.append( struct.pack('>B', 0x02) )
303                 output.append( dictcode(v) )
304
305             else:
306                 v = unicode(v).encode('utf-8')
307                 output.append( struct.pack('>B', 0x01) )
308                 output.append( varint( len(v) ) )
309                 output.append( v )
310
311             output.append( struct.pack('>B', 0x00) )
312
313         output.append( struct.pack('>B', 0x80) )
314
315         return ''.join(output)
316
317     def varint(i):
318         import sys
319
320         bits = []
321         while i:
322             bits.append(i & 0x01)
323             i = i  >> 1
324
325         if not bits:
326             output = [0]
327         else:
328             output = []
329
330         while bits:
331             byte = 0
332             mybits = bits[:7]
333             del bits[:7]
334
335             for bit, p in zip(mybits, itertools.count()):
336                 byte += bit * (2 ** p)
337
338             output.append(byte)
339
340         output[-1] = output[-1] | 0x80
341         return ''.join([chr(b) for b in output])
342
343
344 def getMind():
345     username = config.getTivoUsername()
346     password = config.getTivoPassword()
347
348     if not username or not password:
349        raise Exception("tivo_username and tivo_password required")
350
351     m = Mind(username, password)
352
353     return m
354
Note: See TracBrowser for help on using the browser.