8 import xml
.etree
.ElementTree
as ElementTree
14 def __init__(self
, username
, password
, tsn
):
15 self
.__logger
= logging
.getLogger('pyTivo.mind')
16 self
.__username
= username
17 self
.__password
= password
18 self
.__mind
= config
.get_mind(tsn
)
20 cj
= cookielib
.CookieJar()
21 cp
= urllib2
.HTTPCookieProcessor(cj
)
22 self
.__opener
= urllib2
.build_opener(cp
)
26 def pushVideo(self
, tsn
, url
, description
, duration
, size
,
27 title
, subtitle
, source
='', mime
='video/mpeg',
29 # It looks like tivo only supports one pc per house
30 pc_body_id
= self
.__pcBodySearch
()
36 'bodyId': 'tsn:' + tsn
,
37 'description': description
,
39 'partnerId': 'tivo:pt.3187',
40 'pcBodyId': pc_body_id
,
41 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
48 rating
= metadata
.get_tv(tvrating
)
50 data
['tvRating'] = rating
.lower()
52 mtypes
= {'video/mp4': 'avcL41MP4', 'video/bif': 'vc1ApL3'}
53 data
['encodingType'] = mtypes
.get(mime
, 'mpeg2ProgramStream')
55 data
['url'] = url
+ '?Format=' + mime
58 data
['subtitle'] = subtitle
60 offer_id
, content_id
= self
.__bodyOfferModify
(data
)
61 self
.__subscribe
(offer_id
, content_id
, tsn
)
63 def getDownloadRequests(self
):
79 # It looks like tivo only supports one pc per house
80 pc_body_id
= self
.__pcBodySearch
()
83 offer_list
= self
.__bodyOfferSchedule
(pc_body_id
)
85 for offer
in offer_list
.findall('bodyOffer'):
87 if offer
.findtext('state') != 'scheduled':
90 for n
in NEEDED_VALUES
:
91 d
[n
] = offer
.findtext(n
)
96 def completeDownloadRequest(self
, request
, status
, mime
='video/mpeg'):
98 mtypes
= {'video/mp4': 'avcL41MP4', 'video/bif': 'vc1ApL3'}
99 request
['encodingType'] = mtypes
.get(mime
, 'mpeg2ProgramStream')
100 request
['url'] += '?Format=' + mime
101 request
['state'] = 'complete'
103 request
['state'] = 'cancelled'
104 request
['cancellationReason'] = 'httpFileNotFound'
105 request
['type'] = 'bodyOfferModify'
106 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S',
109 offer_id
, content_id
= self
.__bodyOfferModify
(request
)
111 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
()
117 xml
= self
.__bodyXmppInfoGet
(pc_body_id
)
120 'server': xml
.findtext('server'),
121 'port': int(xml
.findtext('port')),
122 'username': xml
.findtext('xmppId')
125 for sendPresence
in xml
.findall('sendPresence'):
126 results
.setdefault('presence_list',[]).append(sendPresence
.text
)
133 'cams_security_domain': 'tivocom',
134 'cams_login_config': 'http',
135 'cams_cb_username': self
.__username
,
136 'cams_cb_password': self
.__password
,
137 'cams_original_url': '/mind/mind7?type=infoGet'
141 'https://%s/mind/login' % self
.__mind
,
142 urllib
.urlencode(data
)
145 result
= self
.__opener
.open(r
)
149 self
.__logger
.debug('__login\n%s' % (data
))
151 def __dict_request(self
, data
, req
):
153 'https://%s/mind/mind7?type=%s' % (self
.__mind
, req
),
155 {'Content-Type': 'x-tivo/dict-binary'}
157 result
= self
.__opener
.open(r
)
159 xml
= ElementTree
.parse(result
).find('.')
161 self
.__logger
.debug('%s\n%s\n\n%sg' % (req
, data
,
162 ElementTree
.tostring(xml
)))
165 def __bodyOfferModify(self
, data
):
166 """Create an offer"""
168 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&bodyId=' +
171 offer_id
= xml
.findtext('offerId')
173 content_id
= offer_id
.replace('of','ct')
175 return offer_id
, content_id
177 raise Exception(ElementTree
.tostring(xml
))
179 def __subscribe(self
, offer_id
, content_id
, tsn
):
180 """Push the offer to the tivo"""
182 'bodyId': 'tsn:' + tsn
,
184 'contentId': content_id
,
186 'type': 'singleOfferSource'
188 'title': 'pcBodySubscription',
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
):
203 xml
= self
.__dict
_request
({}, 'pcBodySearch')
204 id = xml
.findtext('.//pcBodyId')
206 xml
= self
.__pcBodyStore
('pyTivo', True)
207 id = xml
.findtext('.//pcBodyId')
211 def __collectionIdSearch(self
, url
):
212 """Find collection ids"""
214 xml
= self
.__dict
_request
({'url': url
}, 'collectionIdSearch')
215 return xml
.findtext('collectionId')
217 def __pcBodyStore(self
, name
, replace
=False):
222 'replaceExisting': str(replace
).lower()
225 return self
.__dict
_request
(data
, 'pcBodyStore')
227 def __bodyXmppInfoGet(self
, body_id
):
229 return self
.__dict
_request
({'bodyId': body_id
},
230 'bodyXmppInfoGet&bodyId=' + body_id
)
234 """Helper to create x-tivo/dict-binary"""
237 keys
= [str(k
) for k
in d
]
243 output
.append( varint( len(k
) ) )
246 if isinstance(v
, dict):
247 output
.append( chr(2) )
248 output
.append( dictcode(v
) )
255 if sys
.platform
== 'darwin':
256 v
= v
.decode('macroman')
258 v
= v
.decode('iso8859-1')
259 elif type(v
) != unicode:
261 v
= v
.encode('utf-8')
262 output
.append( chr(1) )
263 output
.append( varint( len(v
) ) )
266 output
.append( chr(0) )
268 output
.append( chr(0x80) )
270 return ''.join(output
)
275 output
.append( chr(i
& 0x7f) )
277 output
.append( chr(i |
0x80) )
278 return ''.join(output
)
280 def getMind(tsn
=None):
281 username
= config
.get_tsn('tivo_username', tsn
)
282 password
= config
.get_tsn('tivo_password', tsn
)
284 if not username
or not password
:
285 raise Exception("tivo_username and tivo_password required")
287 return Mind(username
, password
, tsn
)