10 import xml
.etree
.ElementTree
as ElementTree
13 import elementtree
.ElementTree
as ElementTree
15 warnings
.warn('Python 2.5 or higher or elementtree is needed to use the TivoPush')
17 if 'ElementTree' not in locals():
20 def __init__(self
, *arg
, **karg
):
21 raise Exception('Python 2.5 or higher or elementtree is needed to use the TivoPush')
26 def __init__(self
, username
, password
):
27 self
.__logger
= logging
.getLogger('pyTivo.mind')
28 self
.__username
= username
29 self
.__password
= password
31 self
.__cj
= cookielib
.CookieJar()
32 self
.__opener
= urllib2
.build_opener(urllib2
.HTTPCookieProcessor(self
.__cj
))
36 if not self
.__pcBodySearch
():
37 self
.__pcBodyStore
('pyTivo', True)
39 def pushVideo(self
, tsn
, url
, description
, duration
, size
, title
, subtitle
):
40 # It looks like tivo only supports one pc per house
41 pc_body_id
= self
.__pcBodySearch
()[0]
44 'bodyId': 'tsn:' + tsn
,
45 'description': description
,
47 'encodingType': 'mpeg2ProgramStream',
48 'partnerId': 'tivo:pt.3187',
49 'pcBodyId': pc_body_id
,
50 'publishDate': time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime()),
52 'source': 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
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
()[0]
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
):
96 request
['encodingType'] = 'mpeg2ProgramStream'
97 request
['state'] = 'complete'
98 request
['type'] = 'bodyOfferModify'
99 request
['updateDate'] = time
.strftime('%Y-%m-%d %H:%M%S', time
.gmtime())
101 offer_id
, content_id
= self
.__bodyOfferModify
(request
)
102 self
.__subscribe
(offer_id
, content_id
, request
['bodyId'][4:])
105 def getXMPPLoginInfo(self
):
106 # It looks like tivo only supports one pc per house
107 pc_body_id
= self
.__pcBodySearch
()[0]
109 xml
= self
.__bodyXmppInfoGet
(pc_body_id
)
112 results
['server'] = xml
.findtext('server')
113 results
['port'] = int(xml
.findtext('port'))
114 results
['username'] = xml
.findtext('xmppId')
116 for sendPresence
in xml
.findall('sendPresence'):
117 results
.setdefault('presence_list', []).append(sendPresence
.text
)
124 'cams_security_domain': 'tivocom',
125 'cams_login_config': 'http',
126 'cams_cb_username': self
.__username
,
127 'cams_cb_password': self
.__password
,
128 'cams_original_url': '/mind/mind7?type=infoGet'
132 'https://mind.tivo.com:8181/mind/login',
133 urllib
.urlencode(data
)
136 result
= self
.__opener
.open(r
)
140 self
.__logger
.debug('__login\n%s' % (data
))
142 def __dict_request(self
, data
, req
):
144 'https://mind.tivo.com:8181/mind/mind7?type=' + req
,
146 {'Content-Type': 'x-tivo/dict-binary'}
148 result
= self
.__opener
.open(r
)
150 xml
= ElementTree
.parse(result
).find('.')
152 self
.__logger
.debug('%s\n%s\n\n%sg' % (req
, data
,
153 ElementTree
.tostring(xml
)))
156 def __bodyOfferModify(self
, data
):
157 """Create an offer"""
159 xml
= self
.__dict
_request
(data
, 'bodyOfferModify&bodyId=' +
162 if xml
.findtext('state') != 'complete':
163 raise Exception(ElementTree
.tostring(xml
))
165 offer_id
= xml
.findtext('offerId')
166 content_id
= offer_id
.replace('of','ct')
168 return offer_id
, content_id
171 def __subscribe(self
, offer_id
, content_id
, tsn
):
172 """Push the offer to the tivo"""
174 'bodyId': 'tsn:' + tsn
,
176 'contentId': content_id
,
178 'type': 'singleOfferSource'
180 'title': 'pcBodySubscription',
184 return self
.__dict
_request
(data
, 'subscribe&bodyId=tsn:' + tsn
)
186 def __bodyOfferSchedule(self
, pc_body_id
):
187 """Get pending stuff for this pc"""
189 data
= {'pcBodyId': pc_body_id
}
190 return self
.__dict
_request
(data
, 'bodyOfferSchedule')
192 def __pcBodySearch(self
):
195 xml
= self
.__dict
_request
({}, 'pcBodySearch')
197 return [id.text
for id in xml
.findall('pcBody/pcBodyId')]
199 def __collectionIdSearch(self
, url
):
200 """Find collection ids"""
202 xml
= self
.__dict
_request
({'url': url
}, 'collectionIdSearch')
203 return xml
.findtext('collectionId')
205 def __pcBodyStore(self
, name
, replace
=False):
210 'replaceExisting': str(replace
).lower()
213 return self
.__dict
_request
(data
, 'pcBodyStore')
215 def __bodyXmppInfoGet(self
, body_id
):
217 return self
.__dict
_request
({'bodyId': body_id
},
218 'bodyXmppInfoGet&bodyId=' + body_id
)
222 """Helper to create x-tivo/dict-binary"""
225 keys
= [str(k
) for k
in d
]
231 output
.append( varint( len(k
) ) )
234 if isinstance(v
, dict):
235 output
.append( chr(2) )
236 output
.append( dictcode(v
) )
239 v
= unicode(v
).encode('utf-8')
240 output
.append( chr(1) )
241 output
.append( varint( len(v
) ) )
244 output
.append( chr(0) )
246 output
.append( chr(0x80) )
248 return ''.join(output
)
253 output
.append( chr(i
& 0x7f) )
255 output
.append( chr(i |
0x80) )
256 return ''.join(output
)
259 username
= config
.getTivoUsername()
260 password
= config
.getTivoPassword()
262 if not username
or not password
:
263 raise Exception("tivo_username and tivo_password required")
265 return Mind(username
, password
)