If I don't have optres set we should output what my config file says.
[pyTivo.git] / mind.py
blob7441373a10215f8d950fcd4c39f8ae590ba12597
1 import cookielib
2 import urllib2
3 import urllib
4 import struct
5 import httplib
6 import time
7 import warnings
8 import itertools
10 try:
11 import xml.etree.ElementTree as ElementTree
12 except ImportError:
13 try:
14 import elementtree.ElementTree as ElementTree
15 except ImportError:
16 warnings.warn('Python 2.5 or higher or elementtree is needed to use the TivoPush')
18 if 'ElementTree' not in locals():
20 class Mind:
21 def __init__(self, *arg, **karg):
22 raise Exception('Python 2.5 or higher or elementtree is needed to use the TivoPush')
24 else:
26 class Mind:
27 def __init__(self, username, password, debug=False):
28 self.__username = username
29 self.__password = password
31 self.__debug = debug
33 self.__cj = cookielib.CookieJar()
34 self.__opener = urllib2.build_opener(urllib2.HTTPSHandler(debuglevel=1), urllib2.HTTPCookieProcessor(self.__cj))
36 self.__login()
38 if not self.__pcBodySearch():
39 self.__pcBodyStore('pyTivo', True)
41 def pushVideo(self, tsn, url, description='test', duration='40000', size='3000000', title='test', subtitle='test'):
43 # It looks like tivo only supports one pc per house
44 pc_body_id = self.__pcBodySearch()[0]
45 offer_id, content_id = self.__bodyOfferModify(tsn, pc_body_id, description, duration, size, title, subtitle, url)
46 self.__subscribe(offer_id, content_id, tsn)
48 def bodyOfferSchedule(self, pc_body_id):
50 data = {'pcBodyId' : pc_body_id,}
51 r = urllib2.Request(
52 '/Steph%27s%20Videos/The%20Fairly%20Odd%20Parents%20-%20Channel%20Chasers.xvid-pyro.avi',
53 dictcode(data),
54 {'Content-Type' : 'x-tivo/dict-binary'}
56 result = self.__opener.open(r)
58 self.__log('bodyOfferSchedule\n%s\n\n%sg' % (data, result))
60 return result.read()
62 def __log(self, message):
63 if self.__debug:
64 print message
65 print
67 def __login(self):
69 data = {
70 'cams_security_domain' : 'tivocom',
71 'cams_login_config' : 'http',
72 'cams_cb_username' : self.__username,
73 'cams_cb_password' : self.__password,
74 'cams_original_url' : '/mind/mind7?type=infoGet'
77 r = urllib2.Request(
78 'https://mind.tivo.com:8181/mind/login',
79 urllib.urlencode(data)
81 try:
82 result = self.__opener.open(r)
83 except:
84 pass
86 self.__log('__login\n%s' % (data))
88 def __bodyOfferModify(self, tsn, pc_body_id, description, duration, size, title, subtitle, url):
90 data = {
91 'bodyId' : 'tsn:' + tsn,
92 'description' : description,
93 'duration' : duration,
94 'encodingType' : 'mpeg2ProgramStream',
95 'partnerId' : 'tivo:pt.3187',
96 'pcBodyId' : pc_body_id,
97 'publishDate' : time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
98 'size' : size,
99 'source' : 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
100 'state' : 'complete',
101 'subtitle' : subtitle,
102 'title' : title,
103 'url' : url,
105 r = urllib2.Request(
106 'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferModify&bodyId=tsn:' + tsn,
107 dictcode(data),
108 {'Content-Type' : 'x-tivo/dict-binary'}
110 result = self.__opener.open(r)
112 xml = ElementTree.parse(result).find('.')
114 self.__log('__bodyOfferModify\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
116 if xml.findtext('state') != 'complete':
117 raise Exception(ElementTree.tostring(xml))
119 offer_id = xml.findtext('offerId')
120 content_id = offer_id.replace('of','ct')
122 return offer_id, content_id
125 def __subscribe(self, offer_id, content_id, tsn):
126 data = {
127 'bodyId' : 'tsn:' + tsn,
128 'idSetSource' : {
129 'contentId': content_id,
130 'offerId' : offer_id,
131 'type' : 'singleOfferSource',
133 'title' : 'pcBodySubscription',
134 'uiType' : 'cds',
137 r = urllib2.Request(
138 'https://mind.tivo.com:8181/mind/mind7?type=subscribe&bodyId=tsn:' + tsn,
139 dictcode(data),
140 {'Content-Type' : 'x-tivo/dict-binary'}
142 result = self.__opener.open(r)
144 xml = ElementTree.parse(result).find('.')
146 self.__log('__subscribe\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
148 return xml
150 def __pcBodySearch(self):
152 data = {}
153 r = urllib2.Request(
154 'https://mind.tivo.com:8181/mind/mind7?type=pcBodySearch',
155 dictcode(data),
156 {'Content-Type' : 'x-tivo/dict-binary'}
158 result = self.__opener.open(r)
160 xml = ElementTree.parse(result).find('.')
163 self.__log('__pcBodySearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
165 return [id.text for id in xml.findall('pcBody/pcBodyId')]
167 def __collectionIdSearch(self, url):
169 data = {'url' : url}
170 r = urllib2.Request(
171 'https://mind.tivo.com:8181/mind/mind7?type=collectionIdSearch',
172 dictcode(data),
173 {'Content-Type' : 'x-tivo/dict-binary'}
175 result = self.__opener.open(r)
177 xml = ElementTree.parse( result ).find('.')
178 collection_id = xml.findtext('collectionId')
180 self.__log('__collectionIdSearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
182 return collection_id
184 def __pcBodyStore(self, name, replace=False):
186 data = {
187 'name' : name,
188 'replaceExisting' : str(replace).lower(),
191 r = urllib2.Request(
192 'https://mind.tivo.com:8181/mind/mind7?type=pcBodyStore',
193 dictcode(data),
194 {'Content-Type' : 'x-tivo/dict-binary'}
196 result = self.__opener.open(r)
198 xml = ElementTree.parse(result).find('.')
200 self.__log('__pcBodySearch\n%s\n\n%s' % (data, ElementTree.tostring(xml)))
202 return xml
205 def dictcode(d):
206 """Helper to create x-tivo/dict-binary"""
207 output = []
209 keys = [str(k) for k in d]
210 keys.sort()
212 for k in keys:
213 v = d[k]
215 output.append( varint( len(k) ) )
216 output.append( k )
218 if isinstance(v, dict):
219 output.append( struct.pack('>B', 0x02) )
220 output.append( dictcode(v) )
222 else:
223 v = str(v)
224 output.append( struct.pack('>B', 0x01) )
225 output.append( varint( len(v) ) )
226 output.append( v )
228 output.append( struct.pack('>B', 0x00) )
230 output.append( struct.pack('>B', 0x80) )
232 return ''.join(output)
234 def varint(i):
235 import sys
237 bits = []
238 while i:
239 bits.append(i & 0x01)
240 i = i >> 1
242 if not bits:
243 output = [0]
244 else:
245 output = []
247 while bits:
248 byte = 0
249 mybits = bits[:7]
250 del bits[:7]
252 for bit, p in zip(mybits, itertools.count()):
253 byte += bit * (2 ** p)
255 output.append(byte)
257 output[-1] = output[-1] | 0x80
258 return ''.join([chr(b) for b in output])