7 import xml
.etree
.ElementTree
as ElementTree
13 def __init__(self
, username
, password
, tsn
):
14 self
.__logger
= logging
.getLogger('pyTivo.mind')
15 self
.__username
= username
16 self
.__password
= password
17 self
.__mind
= config
.get_mind(tsn
)
19 cj
= cookielib
.CookieJar()
20 cp
= urllib2
.HTTPCookieProcessor(cj
)
21 self
.__opener
= urllib2
.build_opener(cp
)
25 def pushVideo(self
, tsn
, url
, description
, duration
, size
,
26 title
, subtitle
, source
='', mime
='video/mpeg',
28 # It looks like tivo only supports one pc per house
29 pc_body_id
= self
.__pcBodySearch
()
35 'bodyId': 'tsn:' + tsn
,
36 'description': description
,
38 'partnerId': 'tivo:pt.3187',
39 'pcBodyId': pc_body_id
,
40 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
47 rating
= metadata
.get_tv(tvrating
)
49 data
['tvRating'] = rating
.lower()
51 mtypes
= {'video/mp4': 'avcL41MP4', 'video/bif': 'vc1ApL3'}
52 data
['encodingType'] = mtypes
.get(mime
, 'mpeg2ProgramStream')
54 data
['url'] = url
+ '?Format=' + mime
57 data
['subtitle'] = subtitle
59 offer_id
, content_id
= self
.__bodyOfferModify
(data
)
60 self
.__subscribe
(offer_id
, content_id
, tsn
)
62 def getDownloadRequests(self
):
78 # It looks like tivo only supports one pc per house
79 pc_body_id
= self
.__pcBodySearch
()
82 offer_list
= self
.__bodyOfferSchedule
(pc_body_id
)
84 for offer
in offer_list
.findall('bodyOffer'):
86 if offer
.findtext('state') != 'scheduled':
89 for n
in NEEDED_VALUES
:
90 d
[n
] = offer
.findtext(n
)
95 def completeDownloadRequest(self
, request
, status
, mime
='video/mpeg'):
97 mtypes
= {'video/mp4': 'avcL41MP4', 'video/bif': 'vc1ApL3'}
98 request
['encodingType'] = mtypes
.get(mime
, 'mpeg2ProgramStream')
99 request
['url'] += '?Format=' + mime
100 request
['state'] = 'complete'
102 request
['state'] = 'cancelled'
103 request
['cancellationReason'] = 'httpFileNotFound'
104 request
['type'] = 'bodyOfferModify'
105 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S',
108 offer_id
, content_id
= self
.__bodyOfferModify
(request
)
110 self
.__subscribe
(offer_id
, content_id
, request
['bodyId'][4:])
112 def getXMPPLoginInfo(self
):
113 # It looks like tivo only supports one pc per house
114 pc_body_id
= self
.__pcBodySearch
()
116 xml
= self
.__bodyXmppInfoGet
(pc_body_id
)
119 'server': xml
.findtext('server'),
120 'port': int(xml
.findtext('port')),
121 'username': xml
.findtext('xmppId')
124 for sendPresence
in xml
.findall('sendPresence'):
125 results
.setdefault('presence_list',[]).append(sendPresence
.text
)
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'
140 'https://%s/mind/login' % self
.__mind
,
141 urllib
.urlencode(data
)
144 result
= self
.__opener
.open(r
)
148 self
.__logger
.debug('__login\n%s' % (data
))
150 def __dict_request(self
, data
, req
):
152 'https://%s/mind/mind7?type=%s' % (self
.__mind
, req
),
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
)))
164 def __bodyOfferModify(self
, data
):
165 """Create an offer"""
167 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&bodyId=' +
170 offer_id
= xml
.findtext('offerId')
172 content_id
= offer_id
.replace('of','ct')
174 return offer_id
, content_id
176 raise Exception(ElementTree
.tostring(xml
))
178 def __subscribe(self
, offer_id
, content_id
, tsn
):
179 """Push the offer to the tivo"""
181 'bodyId': 'tsn:' + tsn
,
183 'contentId': content_id
,
185 'type': 'singleOfferSource'
187 'title': 'pcBodySubscription',
191 return self
.__dict
_request
(data
, 'subscribe&bodyId=tsn:' + tsn
)
193 def __bodyOfferSchedule(self
, pc_body_id
):
194 """Get pending stuff for this pc"""
196 data
= {'pcBodyId': pc_body_id
}
197 return self
.__dict
_request
(data
, 'bodyOfferSchedule')
199 def __pcBodySearch(self
):
202 xml
= self
.__dict
_request
({}, 'pcBodySearch')
203 id = xml
.findtext('.//pcBodyId')
205 xml
= self
.__pcBodyStore
('pyTivo', True)
206 id = xml
.findtext('.//pcBodyId')
210 def __collectionIdSearch(self
, url
):
211 """Find collection ids"""
213 xml
= self
.__dict
_request
({'url': url
}, 'collectionIdSearch')
214 return xml
.findtext('collectionId')
216 def __pcBodyStore(self
, name
, replace
=False):
221 'replaceExisting': str(replace
).lower()
224 return self
.__dict
_request
(data
, 'pcBodyStore')
226 def __bodyXmppInfoGet(self
, body_id
):
228 return self
.__dict
_request
({'bodyId': body_id
},
229 'bodyXmppInfoGet&bodyId=' + body_id
)
233 """Helper to create x-tivo/dict-binary"""
236 keys
= [str(k
) for k
in d
]
242 output
.append( varint( len(k
) ) )
245 if isinstance(v
, dict):
246 output
.append( chr(2) )
247 output
.append( dictcode(v
) )
254 if sys
.platform
== 'darwin':
255 v
= v
.decode('macroman')
257 v
= v
.decode('iso8859-1')
258 elif type(v
) != unicode:
260 v
= v
.encode('utf-8')
261 output
.append( chr(1) )
262 output
.append( varint( len(v
) ) )
265 output
.append( chr(0) )
267 output
.append( chr(0x80) )
269 return ''.join(output
)
274 output
.append( chr(i
& 0x7f) )
276 output
.append( chr(i |
0x80) )
277 return ''.join(output
)
279 def getMind(tsn
=None):
280 username
= config
.get_tsn('tivo_username', tsn
)
281 password
= config
.get_tsn('tivo_password', tsn
)
283 if not username
or not password
:
284 raise Exception("tivo_username and tivo_password required")
286 return Mind(username
, password
, tsn
)