Reap dead ffmpegs; timeout 10 minutes.
[pyTivo/TheBayer.git] / mind.py
blob48309c48d6f4f84880ccb0d36d491afa197b1cc9
1 import cookielib
2 import logging
3 import sys
4 import time
5 import urllib2
6 import urllib
7 import warnings
9 import config
11 try:
12 import xml.etree.ElementTree as ElementTree
13 except ImportError:
14 try:
15 import elementtree.ElementTree as ElementTree
16 except ImportError:
17 warnings.warn('Python 2.5 or higher or elementtree is ' +
18 'needed to use the TivoPush')
20 if 'ElementTree' not in locals():
22 class Mind:
23 def __init__(self, *arg, **karg):
24 raise Exception('Python 2.5 or higher or elementtree is ' +
25 'needed to use the TivoPush')
27 else:
29 class Mind:
30 def __init__(self, username, password):
31 self.__logger = logging.getLogger('pyTivo.mind')
32 self.__username = username
33 self.__password = password
35 cj = cookielib.CookieJar()
36 cp = urllib2.HTTPCookieProcessor(cj)
37 self.__opener = urllib2.build_opener(cp)
39 self.__login()
41 if not self.__pcBodySearch():
42 self.__pcBodyStore('pyTivo', True)
44 def pushVideo(self, tsn, url, description, duration, size,
45 title, subtitle, source='', mime='video/mpeg'):
46 # It looks like tivo only supports one pc per house
47 pc_body_id = self.__pcBodySearch()[0]
49 if not source:
50 source = title
52 data = {
53 'bodyId': 'tsn:' + tsn,
54 'description': description,
55 'duration': duration,
56 'partnerId': 'tivo:pt.3187',
57 'pcBodyId': pc_body_id,
58 'publishDate': time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
59 'size': size,
60 'source': source,
61 'state': 'complete',
62 'title': title
65 if mime == 'video/mp4':
66 data['encodingType'] = 'avcL41MP4'
67 elif mime == 'video/bif':
68 data['encodingType'] = 'vc1ApL3'
69 else:
70 data['encodingType'] = 'mpeg2ProgramStream'
72 data['url'] = url + '?Format=' + mime
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='video/mpeg'):
114 if mime == 'video/mp4':
115 request['encodingType'] = 'avcL41MP4'
116 elif mime == 'video/bif':
117 request['encodingType'] = 'vc1ApL3'
118 else:
119 request['encodingType'] = 'mpeg2ProgramStream'
120 request['url'] = url + '?Format=' + mime
121 request['state'] = 'complete'
122 request['type'] = 'bodyOfferModify'
123 request['updateDate'] = time.strftime('%Y-%m-%d %H:%M%S',
124 time.gmtime())
126 offer_id, content_id = self.__bodyOfferModify(request)
127 self.__subscribe(offer_id, content_id, request['bodyId'][4:])
129 def getXMPPLoginInfo(self):
130 # It looks like tivo only supports one pc per house
131 pc_body_id = self.__pcBodySearch()[0]
133 xml = self.__bodyXmppInfoGet(pc_body_id)
135 results = {
136 'server': xml.findtext('server'),
137 'port': int(xml.findtext('port')),
138 'username': xml.findtext('xmppId')
141 for sendPresence in xml.findall('sendPresence'):
142 results.setdefault('presence_list',[]).append(sendPresence.text)
144 return results
146 def __login(self):
148 data = {
149 'cams_security_domain': 'tivocom',
150 'cams_login_config': 'http',
151 'cams_cb_username': self.__username,
152 'cams_cb_password': self.__password,
153 'cams_original_url': '/mind/mind7?type=infoGet'
156 r = urllib2.Request(
157 'https://mind.tivo.com:8181/mind/login',
158 urllib.urlencode(data)
160 try:
161 result = self.__opener.open(r)
162 except:
163 pass
165 self.__logger.debug('__login\n%s' % (data))
167 def __dict_request(self, data, req):
168 r = urllib2.Request(
169 'https://mind.tivo.com:8181/mind/mind7?type=' + req,
170 dictcode(data),
171 {'Content-Type': 'x-tivo/dict-binary'}
173 result = self.__opener.open(r)
175 xml = ElementTree.parse(result).find('.')
177 self.__logger.debug('%s\n%s\n\n%sg' % (req, data,
178 ElementTree.tostring(xml)))
179 return xml
181 def __bodyOfferModify(self, data):
182 """Create an offer"""
184 xml = self.__dict_request(data, 'bodyOfferModify&bodyId=' +
185 data['bodyId'])
187 if xml.findtext('state') != 'complete':
188 raise Exception(ElementTree.tostring(xml))
190 offer_id = xml.findtext('offerId')
191 content_id = offer_id.replace('of','ct')
193 return offer_id, content_id
196 def __subscribe(self, offer_id, content_id, tsn):
197 """Push the offer to the tivo"""
198 data = {
199 'bodyId': 'tsn:' + tsn,
200 'idSetSource': {
201 'contentId': content_id,
202 'offerId': offer_id,
203 'type': 'singleOfferSource'
205 'title': 'pcBodySubscription',
206 'uiType': 'cds'
209 return self.__dict_request(data, 'subscribe&bodyId=tsn:' + tsn)
211 def __bodyOfferSchedule(self, pc_body_id):
212 """Get pending stuff for this pc"""
214 data = {'pcBodyId': pc_body_id}
215 return self.__dict_request(data, 'bodyOfferSchedule')
217 def __pcBodySearch(self):
218 """Find PCS"""
220 xml = self.__dict_request({}, 'pcBodySearch')
222 return [id.text for id in xml.findall('pcBody/pcBodyId')]
224 def __collectionIdSearch(self, url):
225 """Find collection ids"""
227 xml = self.__dict_request({'url': url}, 'collectionIdSearch')
228 return xml.findtext('collectionId')
230 def __pcBodyStore(self, name, replace=False):
231 """Setup a new PC"""
233 data = {
234 'name': name,
235 'replaceExisting': str(replace).lower()
238 return self.__dict_request(data, 'pcBodyStore')
240 def __bodyXmppInfoGet(self, body_id):
242 return self.__dict_request({'bodyId': body_id},
243 'bodyXmppInfoGet&bodyId=' + body_id)
246 def dictcode(d):
247 """Helper to create x-tivo/dict-binary"""
248 output = []
250 keys = [str(k) for k in d]
251 keys.sort()
253 for k in keys:
254 v = d[k]
256 output.append( varint( len(k) ) )
257 output.append( k )
259 if isinstance(v, dict):
260 output.append( chr(2) )
261 output.append( dictcode(v) )
263 else:
264 if type(v) == str:
265 try:
266 v = v.decode('utf8')
267 except:
268 if sys.platform == 'darwin':
269 v = v.decode('macroman')
270 else:
271 v = v.decode('iso8859-1')
272 elif type(v) != unicode:
273 v = str(v)
274 v = v.encode('utf-8')
275 output.append( chr(1) )
276 output.append( varint( len(v) ) )
277 output.append( v )
279 output.append( chr(0) )
281 output.append( chr(0x80) )
283 return ''.join(output)
285 def varint(i):
286 output = []
287 while i > 0x7f:
288 output.append( chr(i & 0x7f) )
289 i >>= 7
290 output.append( chr(i | 0x80) )
291 return ''.join(output)
293 def getMind(tsn=None):
294 username = config.get_tsn('tivo_username', tsn)
295 password = config.get_tsn('tivo_password', tsn)
297 if not username or not password:
298 raise Exception("tivo_username and tivo_password required")
300 return Mind(username, password)