11 import xml
.etree
.ElementTree
as ElementTree
14 import elementtree
.ElementTree
as ElementTree
16 warnings
.warn('Python 2.5 or higher or elementtree is ' +
17 'needed to use the TivoPush')
19 if 'ElementTree' not in locals():
22 def __init__(self
, *arg
, **karg
):
23 raise Exception('Python 2.5 or higher or elementtree is ' +
24 'needed to use the TivoPush')
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
)
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]
52 'bodyId': 'tsn:' + tsn
,
53 'description': description
,
55 'partnerId': 'tivo:pt.3187',
56 'pcBodyId': pc_body_id
,
57 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
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
71 data
['encodingType'] = 'mpeg2ProgramStream'
75 data
['subtitle'] = subtitle
77 offer_id
, content_id
= self
.__bodyOfferModify
(data
)
78 self
.__subscribe
(offer_id
, content_id
, tsn
)
80 def getDownloadRequests(self
):
96 # It looks like tivo only supports one pc per house
97 pc_body_id
= self
.__pcBodySearch
()[0]
100 offer_list
= self
.__bodyOfferSchedule
(pc_body_id
)
102 for offer
in offer_list
.findall('bodyOffer'):
104 if offer
.findtext('state') != 'scheduled':
107 for n
in NEEDED_VALUES
:
108 d
[n
] = offer
.findtext(n
)
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
121 request
['encodingType'] = 'mpeg2ProgramStream'
122 request
['state'] = 'complete'
123 request
['type'] = 'bodyOfferModify'
124 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S',
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
)
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
)
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'
158 'https://mind.tivo.com:8181/mind/login',
159 urllib
.urlencode(data
)
162 result
= self
.__opener
.open(r
)
166 self
.__logger
.debug('__login\n%s' % (data
))
168 def __dict_request(self
, data
, req
):
170 'https://mind.tivo.com:8181/mind/mind7?type=' + req
,
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
)))
182 def __bodyOfferModify(self
, data
):
183 """Create an offer"""
185 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&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"""
200 'bodyId': 'tsn:' + tsn
,
202 'contentId': content_id
,
204 'type': 'singleOfferSource'
206 'title': 'pcBodySubscription',
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
):
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):
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
)
248 """Helper to create x-tivo/dict-binary"""
251 keys
= [str(k
) for k
in d
]
257 output
.append( varint( len(k
) ) )
260 if isinstance(v
, dict):
261 output
.append( chr(2) )
262 output
.append( dictcode(v
) )
265 v
= unicode(v
).encode('utf-8')
266 output
.append( chr(1) )
267 output
.append( varint( len(v
) ) )
270 output
.append( chr(0) )
272 output
.append( chr(0x80) )
274 return ''.join(output
)
279 output
.append( chr(i
& 0x7f) )
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
)