12 import xml
.etree
.ElementTree
as ElementTree
15 import elementtree
.ElementTree
as ElementTree
17 warnings
.warn('Python 2.5 or higher or elementtree is ' +
18 'needed to use the TivoPush')
20 if 'ElementTree' not in locals():
23 def __init__(self
, *arg
, **karg
):
24 raise Exception('Python 2.5 or higher or elementtree is ' +
25 'needed to use the TivoPush')
30 def __init__(self
, username
, password
, tsn
):
31 self
.__logger
= logging
.getLogger('pyTivo.mind')
32 self
.__username
= username
33 self
.__password
= password
34 self
.__mind
= config
.get_mind(tsn
)
36 cj
= cookielib
.CookieJar()
37 cp
= urllib2
.HTTPCookieProcessor(cj
)
38 self
.__opener
= urllib2
.build_opener(cp
)
42 if not self
.__pcBodySearch
():
43 self
.__pcBodyStore
('pyTivo', True)
45 def pushVideo(self
, tsn
, url
, description
, duration
, size
,
46 title
, subtitle
, source
='', mime
='video/mpeg',
48 # It looks like tivo only supports one pc per house
49 pc_body_id
= self
.__pcBodySearch
()[0]
55 'bodyId': 'tsn:' + tsn
,
56 'description': description
,
58 'partnerId': 'tivo:pt.3187',
59 'pcBodyId': pc_body_id
,
60 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
67 ratings
= {'x1': 'y7', 'x2': 'y', 'x3': 'g', 'x4': 'pg',
68 'x5': '14', 'x6': 'ma', 'x7': 'nr'}
69 if tvrating
in ratings
:
70 data
['tvRating'] = ratings
[tvrating
]
72 if mime
== 'video/mp4':
73 data
['encodingType'] = 'avcL41MP4'
74 elif mime
== 'video/bif':
75 data
['encodingType'] = 'vc1ApL3'
77 data
['encodingType'] = 'mpeg2ProgramStream'
79 data
['url'] = url
+ '?Format=' + mime
82 data
['subtitle'] = subtitle
84 offer_id
, content_id
= self
.__bodyOfferModify
(data
)
85 self
.__subscribe
(offer_id
, content_id
, tsn
)
87 def getDownloadRequests(self
):
103 # It looks like tivo only supports one pc per house
104 pc_body_id
= self
.__pcBodySearch
()[0]
107 offer_list
= self
.__bodyOfferSchedule
(pc_body_id
)
109 for offer
in offer_list
.findall('bodyOffer'):
111 if offer
.findtext('state') != 'scheduled':
114 for n
in NEEDED_VALUES
:
115 d
[n
] = offer
.findtext(n
)
120 def completeDownloadRequest(self
, request
, status
, mime
='video/mpeg'):
122 if mime
== 'video/mp4':
123 request
['encodingType'] = 'avcL41MP4'
124 elif mime
== 'video/bif':
125 request
['encodingType'] = 'vc1ApL3'
127 request
['encodingType'] = 'mpeg2ProgramStream'
128 request
['url'] += '?Format=' + mime
129 request
['state'] = 'complete'
131 request
['state'] = 'cancelled'
132 request
['cancellationReason'] = 'httpFileNotFound'
133 request
['type'] = 'bodyOfferModify'
134 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S',
137 offer_id
, content_id
= self
.__bodyOfferModify
(request
)
139 self
.__subscribe
(offer_id
, content_id
, request
['bodyId'][4:])
141 def getXMPPLoginInfo(self
):
142 # It looks like tivo only supports one pc per house
143 pc_body_id
= self
.__pcBodySearch
()[0]
145 xml
= self
.__bodyXmppInfoGet
(pc_body_id
)
148 'server': xml
.findtext('server'),
149 'port': int(xml
.findtext('port')),
150 'username': xml
.findtext('xmppId')
153 for sendPresence
in xml
.findall('sendPresence'):
154 results
.setdefault('presence_list',[]).append(sendPresence
.text
)
161 'cams_security_domain': 'tivocom',
162 'cams_login_config': 'http',
163 'cams_cb_username': self
.__username
,
164 'cams_cb_password': self
.__password
,
165 'cams_original_url': '/mind/mind7?type=infoGet'
169 'https://%s/mind/login' % self
.__mind
,
170 urllib
.urlencode(data
)
173 result
= self
.__opener
.open(r
)
177 self
.__logger
.debug('__login\n%s' % (data
))
179 def __dict_request(self
, data
, req
):
181 'https://%s/mind/mind7?type=%s' % (self
.__mind
, req
),
183 {'Content-Type': 'x-tivo/dict-binary'}
185 result
= self
.__opener
.open(r
)
187 xml
= ElementTree
.parse(result
).find('.')
189 self
.__logger
.debug('%s\n%s\n\n%sg' % (req
, data
,
190 ElementTree
.tostring(xml
)))
193 def __bodyOfferModify(self
, data
):
194 """Create an offer"""
196 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&bodyId=' +
199 offer_id
= xml
.findtext('offerId')
200 content_id
= offer_id
.replace('of','ct')
202 return offer_id
, content_id
204 def __subscribe(self
, offer_id
, content_id
, tsn
):
205 """Push the offer to the tivo"""
207 'bodyId': 'tsn:' + tsn
,
209 'contentId': content_id
,
211 'type': 'singleOfferSource'
213 'title': 'pcBodySubscription',
217 return self
.__dict
_request
(data
, 'subscribe&bodyId=tsn:' + tsn
)
219 def __bodyOfferSchedule(self
, pc_body_id
):
220 """Get pending stuff for this pc"""
222 data
= {'pcBodyId': pc_body_id
}
223 return self
.__dict
_request
(data
, 'bodyOfferSchedule')
225 def __pcBodySearch(self
):
228 xml
= self
.__dict
_request
({}, 'pcBodySearch')
230 return [id.text
for id in xml
.findall('pcBody/pcBodyId')]
232 def __collectionIdSearch(self
, url
):
233 """Find collection ids"""
235 xml
= self
.__dict
_request
({'url': url
}, 'collectionIdSearch')
236 return xml
.findtext('collectionId')
238 def __pcBodyStore(self
, name
, replace
=False):
243 'replaceExisting': str(replace
).lower()
246 return self
.__dict
_request
(data
, 'pcBodyStore')
248 def __bodyXmppInfoGet(self
, body_id
):
250 return self
.__dict
_request
({'bodyId': body_id
},
251 'bodyXmppInfoGet&bodyId=' + body_id
)
255 """Helper to create x-tivo/dict-binary"""
258 keys
= [str(k
) for k
in d
]
264 output
.append( varint( len(k
) ) )
267 if isinstance(v
, dict):
268 output
.append( chr(2) )
269 output
.append( dictcode(v
) )
276 if sys
.platform
== 'darwin':
277 v
= v
.decode('macroman')
279 v
= v
.decode('iso8859-1')
280 elif type(v
) != unicode:
282 v
= v
.encode('utf-8')
283 output
.append( chr(1) )
284 output
.append( varint( len(v
) ) )
287 output
.append( chr(0) )
289 output
.append( chr(0x80) )
291 return ''.join(output
)
296 output
.append( chr(i
& 0x7f) )
298 output
.append( chr(i |
0x80) )
299 return ''.join(output
)
301 def getMind(tsn
=None):
302 username
= config
.get_tsn('tivo_username', tsn
)
303 password
= config
.get_tsn('tivo_password', tsn
)
305 if not username
or not password
:
306 raise Exception("tivo_username and tivo_password required")
308 return Mind(username
, password
, tsn
)