10 import xml
.etree
.ElementTree
as ElementTree
13 import elementtree
.ElementTree
as ElementTree
15 warnings
.warn('Python 2.5 or higher or elementtree is ' +
16 'needed to use the TivoPush')
18 if 'ElementTree' not in locals():
21 def __init__(self
, *arg
, **karg
):
22 raise Exception('Python 2.5 or higher or elementtree is ' +
23 'needed to use the TivoPush')
28 def __init__(self
, username
, password
):
29 self
.__logger
= logging
.getLogger('pyTivo.mind')
30 self
.__username
= username
31 self
.__password
= password
33 cj
= cookielib
.CookieJar()
34 cp
= urllib2
.HTTPCookieProcessor(cj
)
35 self
.__opener
= urllib2
.build_opener(cp
)
39 if not self
.__pcBodySearch
():
40 self
.__pcBodyStore
('pyTivo', True)
42 def pushVideo(self
, tsn
, url
, description
, duration
, size
,
44 # It looks like tivo only supports one pc per house
45 pc_body_id
= self
.__pcBodySearch
()[0]
48 'bodyId': 'tsn:' + tsn
,
49 'description': description
,
51 'encodingType': 'mpeg2ProgramStream',
52 'partnerId': 'tivo:pt.3187',
53 'pcBodyId': pc_body_id
,
54 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
56 'source': 'file:/C%3A%2FDocuments%20and%20Settings%2F' +
57 'Stephanie%2FDesktop%2FVideo',
64 offer_id
, content_id
= self
.__bodyOfferModify
(data
)
65 self
.__subscribe
(offer_id
, content_id
, tsn
)
67 def getDownloadRequests(self
):
83 # It looks like tivo only supports one pc per house
84 pc_body_id
= self
.__pcBodySearch
()[0]
87 offer_list
= self
.__bodyOfferSchedule
(pc_body_id
)
89 for offer
in offer_list
.findall('bodyOffer'):
91 if offer
.findtext('state') != 'scheduled':
94 for n
in NEEDED_VALUES
:
95 d
[n
] = offer
.findtext(n
)
100 def completeDownloadRequest(self
, request
):
101 request
['encodingType'] = 'mpeg2ProgramStream'
102 request
['state'] = 'complete'
103 request
['type'] = 'bodyOfferModify'
104 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S',
107 offer_id
, content_id
= self
.__bodyOfferModify
(request
)
108 self
.__subscribe
(offer_id
, content_id
, request
['bodyId'][4:])
111 def getXMPPLoginInfo(self
):
112 # It looks like tivo only supports one pc per house
113 pc_body_id
= self
.__pcBodySearch
()[0]
115 xml
= self
.__bodyXmppInfoGet
(pc_body_id
)
118 results
['server'] = xml
.findtext('server')
119 results
['port'] = int(xml
.findtext('port'))
120 results
['username'] = xml
.findtext('xmppId')
122 for sendPresence
in xml
.findall('sendPresence'):
123 results
.setdefault('presence_list',[]).append(sendPresence
.text
)
130 'cams_security_domain': 'tivocom',
131 'cams_login_config': 'http',
132 'cams_cb_username': self
.__username
,
133 'cams_cb_password': self
.__password
,
134 'cams_original_url': '/mind/mind7?type=infoGet'
138 'https://mind.tivo.com:8181/mind/login',
139 urllib
.urlencode(data
)
142 result
= self
.__opener
.open(r
)
146 self
.__logger
.debug('__login\n%s' % (data
))
148 def __dict_request(self
, data
, req
):
150 'https://mind.tivo.com:8181/mind/mind7?type=' + req
,
152 {'Content-Type': 'x-tivo/dict-binary'}
154 result
= self
.__opener
.open(r
)
156 xml
= ElementTree
.parse(result
).find('.')
158 self
.__logger
.debug('%s\n%s\n\n%sg' % (req
, data
,
159 ElementTree
.tostring(xml
)))
162 def __bodyOfferModify(self
, data
):
163 """Create an offer"""
165 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&bodyId=' +
168 if xml
.findtext('state') != 'complete':
169 raise Exception(ElementTree
.tostring(xml
))
171 offer_id
= xml
.findtext('offerId')
172 content_id
= offer_id
.replace('of','ct')
174 return offer_id
, content_id
177 def __subscribe(self
, offer_id
, content_id
, tsn
):
178 """Push the offer to the tivo"""
180 'bodyId': 'tsn:' + tsn
,
182 'contentId': content_id
,
184 'type': 'singleOfferSource'
186 'title': 'pcBodySubscription',
190 return self
.__dict
_request
(data
, 'subscribe&bodyId=tsn:' + tsn
)
192 def __bodyOfferSchedule(self
, pc_body_id
):
193 """Get pending stuff for this pc"""
195 data
= {'pcBodyId': pc_body_id
}
196 return self
.__dict
_request
(data
, 'bodyOfferSchedule')
198 def __pcBodySearch(self
):
201 xml
= self
.__dict
_request
({}, 'pcBodySearch')
203 return [id.text
for id in xml
.findall('pcBody/pcBodyId')]
205 def __collectionIdSearch(self
, url
):
206 """Find collection ids"""
208 xml
= self
.__dict
_request
({'url': url
}, 'collectionIdSearch')
209 return xml
.findtext('collectionId')
211 def __pcBodyStore(self
, name
, replace
=False):
216 'replaceExisting': str(replace
).lower()
219 return self
.__dict
_request
(data
, 'pcBodyStore')
221 def __bodyXmppInfoGet(self
, body_id
):
223 return self
.__dict
_request
({'bodyId': body_id
},
224 'bodyXmppInfoGet&bodyId=' + body_id
)
228 """Helper to create x-tivo/dict-binary"""
231 keys
= [str(k
) for k
in d
]
237 output
.append( varint( len(k
) ) )
240 if isinstance(v
, dict):
241 output
.append( chr(2) )
242 output
.append( dictcode(v
) )
245 v
= unicode(v
).encode('utf-8')
246 output
.append( chr(1) )
247 output
.append( varint( len(v
) ) )
250 output
.append( chr(0) )
252 output
.append( chr(0x80) )
254 return ''.join(output
)
259 output
.append( chr(i
& 0x7f) )
261 output
.append( chr(i |
0x80) )
262 return ''.join(output
)
265 username
= config
.getTivoUsername()
266 password
= config
.getTivoPassword()
268 if not username
or not password
:
269 raise Exception("tivo_username and tivo_password required")
271 return Mind(username
, password
)