Error in pyTivo.conf.dist -- should be ffmpeg_pram, not ffmpeg_prams.
[pyTivo/wmcbrine.git] / mind.py
blob49130a096be38fd8bc29ae91c479f2058ff2c253
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):
45 # It looks like tivo only supports one pc per house
46 pc_body_id = self.__pcBodySearch()[0]
48 data = {
49 'bodyId': 'tsn:' + tsn,
50 'description': description,
51 'duration': duration,
52 'encodingType': 'mpeg2ProgramStream',
53 'partnerId': 'tivo:pt.3187',
54 'pcBodyId': pc_body_id,
55 'publishDate': time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
56 'size': size,
57 'source': title,
58 'state': 'complete',
59 'title': title,
60 'url': url
63 if subtitle:
64 data['subtitle'] = subtitle
66 offer_id, content_id = self.__bodyOfferModify(data)
67 self.__subscribe(offer_id, content_id, tsn)
69 def getDownloadRequests(self):
70 NEEDED_VALUES = [
71 'bodyId',
72 'bodyOfferId',
73 'description',
74 'partnerId',
75 'pcBodyId',
76 'publishDate',
77 'source',
78 'state',
79 'subscriptionId',
80 'subtitle',
81 'title',
82 'url'
85 # It looks like tivo only supports one pc per house
86 pc_body_id = self.__pcBodySearch()[0]
88 requests = []
89 offer_list = self.__bodyOfferSchedule(pc_body_id)
91 for offer in offer_list.findall('bodyOffer'):
92 d = {}
93 if offer.findtext('state') != 'scheduled':
94 continue
96 for n in NEEDED_VALUES:
97 d[n] = offer.findtext(n)
98 requests.append(d)
100 return requests
102 def completeDownloadRequest(self, request):
103 request['encodingType'] = 'mpeg2ProgramStream'
104 request['state'] = 'complete'
105 request['type'] = 'bodyOfferModify'
106 request['updateDate'] = time.strftime('%Y-%m-%d %H:%M%S',
107 time.gmtime())
109 offer_id, content_id = self.__bodyOfferModify(request)
110 self.__subscribe(offer_id, content_id, request['bodyId'][4:])
113 def getXMPPLoginInfo(self):
114 # It looks like tivo only supports one pc per house
115 pc_body_id = self.__pcBodySearch()[0]
117 xml = self.__bodyXmppInfoGet(pc_body_id)
119 results = {}
120 results['server'] = xml.findtext('server')
121 results['port'] = int(xml.findtext('port'))
122 results['username'] = xml.findtext('xmppId')
124 for sendPresence in xml.findall('sendPresence'):
125 results.setdefault('presence_list',[]).append(sendPresence.text)
127 return results
129 def __login(self):
131 data = {
132 'cams_security_domain': 'tivocom',
133 'cams_login_config': 'http',
134 'cams_cb_username': self.__username,
135 'cams_cb_password': self.__password,
136 'cams_original_url': '/mind/mind7?type=infoGet'
139 r = urllib2.Request(
140 'https://mind.tivo.com:8181/mind/login',
141 urllib.urlencode(data)
143 try:
144 result = self.__opener.open(r)
145 except:
146 pass
148 self.__logger.debug('__login\n%s' % (data))
150 def __dict_request(self, data, req):
151 r = urllib2.Request(
152 'https://mind.tivo.com:8181/mind/mind7?type=' + req,
153 dictcode(data),
154 {'Content-Type': 'x-tivo/dict-binary'}
156 result = self.__opener.open(r)
158 xml = ElementTree.parse(result).find('.')
160 self.__logger.debug('%s\n%s\n\n%sg' % (req, data,
161 ElementTree.tostring(xml)))
162 return xml
164 def __bodyOfferModify(self, data):
165 """Create an offer"""
167 xml = self.__dict_request(data, 'bodyOfferModify&bodyId=' +
168 data['bodyId'])
170 if xml.findtext('state') != 'complete':
171 raise Exception(ElementTree.tostring(xml))
173 offer_id = xml.findtext('offerId')
174 content_id = offer_id.replace('of','ct')
176 return offer_id, content_id
179 def __subscribe(self, offer_id, content_id, tsn):
180 """Push the offer to the tivo"""
181 data = {
182 'bodyId': 'tsn:' + tsn,
183 'idSetSource': {
184 'contentId': content_id,
185 'offerId': offer_id,
186 'type': 'singleOfferSource'
188 'title': 'pcBodySubscription',
189 'uiType': 'cds'
192 return self.__dict_request(data, 'subscribe&bodyId=tsn:' + tsn)
194 def __bodyOfferSchedule(self, pc_body_id):
195 """Get pending stuff for this pc"""
197 data = {'pcBodyId': pc_body_id}
198 return self.__dict_request(data, 'bodyOfferSchedule')
200 def __pcBodySearch(self):
201 """Find PCS"""
203 xml = self.__dict_request({}, 'pcBodySearch')
205 return [id.text for id in xml.findall('pcBody/pcBodyId')]
207 def __collectionIdSearch(self, url):
208 """Find collection ids"""
210 xml = self.__dict_request({'url': url}, 'collectionIdSearch')
211 return xml.findtext('collectionId')
213 def __pcBodyStore(self, name, replace=False):
214 """Setup a new PC"""
216 data = {
217 'name': name,
218 'replaceExisting': str(replace).lower()
221 return self.__dict_request(data, 'pcBodyStore')
223 def __bodyXmppInfoGet(self, body_id):
225 return self.__dict_request({'bodyId': body_id},
226 'bodyXmppInfoGet&bodyId=' + body_id)
229 def dictcode(d):
230 """Helper to create x-tivo/dict-binary"""
231 output = []
233 keys = [str(k) for k in d]
234 keys.sort()
236 for k in keys:
237 v = d[k]
239 output.append( varint( len(k) ) )
240 output.append( k )
242 if isinstance(v, dict):
243 output.append( chr(2) )
244 output.append( dictcode(v) )
246 else:
247 v = unicode(v).encode('utf-8')
248 output.append( chr(1) )
249 output.append( varint( len(v) ) )
250 output.append( v )
252 output.append( chr(0) )
254 output.append( chr(0x80) )
256 return ''.join(output)
258 def varint(i):
259 output = []
260 while i > 0x7f:
261 output.append( chr(i & 0x7f) )
262 i >>= 7
263 output.append( chr(i | 0x80) )
264 return ''.join(output)
266 def getMind():
267 username = config.getTivoUsername()
268 password = config.getTivoPassword()
270 if not username or not password:
271 raise Exception("tivo_username and tivo_password required")
273 return Mind(username, password)