Merge commit 'krkeegan/master'
[pyTivo.git] / mind.py
blobd048738d0f1e45e0125a6308b5b9c00cd5ed424b
1 import cookielib
2 import urllib2
3 import urllib
4 import struct
5 import httplib
6 import xml.etree.ElementTree as ElementTree
7 import time
10 class Mind:
11 def __init__(self, username, password, debug=False):
12 self.__username = username
13 self.__password = password
15 self.__debug = debug
17 self.__cj = cookielib.CookieJar()
18 self.__opener = urllib2.build_opener(urllib2.HTTPSHandler(debuglevel=1), urllib2.HTTPCookieProcessor(self.__cj))
20 self.__login()
22 if not self.__pcBodySearch():
23 self.__pcBodyStore('pyTivo', True)
25 def pushVideo(self, tsn, url, description='test', duration='40000', size='3000000', title='test', subtitle='test'):
27 # It looks like tivo only supports one pc per house
28 pc_body_id = self.__pcBodySearch()[0]
29 offer_id, content_id = self.__bodyOfferModify(tsn, pc_body_id, description, duration, size, title, subtitle, url)
30 self.__subscribe(offer_id, content_id, tsn)
32 def bodyOfferSchedule(self, pc_body_id):
34 data = {'pcBodyId' : pc_body_id,}
35 r = urllib2.Request(
36 '/Steph%27s%20Videos/The%20Fairly%20Odd%20Parents%20-%20Channel%20Chasers.xvid-pyro.avi',
37 dictcode(data),
38 {'Content-Type' : 'x-tivo/dict-binary'}
40 result = self.__opener.open(r)
42 self.__log('bodyOfferSchedule\n%s\n\n%sg' % (data, result))
44 return result.read()
46 def __log(self, message):
47 if self.__debug:
48 print message
49 print
51 def __login(self):
53 data = {
54 'cams_security_domain' : 'tivocom',
55 'cams_login_config' : 'http',
56 'cams_cb_username' : self.__username,
57 'cams_cb_password' : self.__password,
58 'cams_original_url' : '/mind/mind7?type=infoGet'
61 r = urllib2.Request(
62 'https://mind.tivo.com:8181/mind/login',
63 urllib.urlencode(data)
65 try:
66 result = self.__opener.open(r)
67 except:
68 pass
70 self.__log('__login\n%s' % (data))
72 def __bodyOfferModify(self, tsn, pc_body_id, description, duration, size, title, subtitle, url):
74 data = {
75 'bodyId' : 'tsn:' + tsn,
76 'description' : description,
77 'duration' : duration,
78 'encodingType' : 'mpeg2ProgramStream',
79 'partnerId' : 'tivo:pt.3187',
80 'pcBodyId' : pc_body_id,
81 'publishDate' : time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
82 'size' : size,
83 'source' : 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
84 'state' : 'complete',
85 'subtitle' : subtitle,
86 'title' : title,
87 'url' : url,
89 r = urllib2.Request(
90 'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferModify&bodyId=tsn:' + tsn,
91 dictcode(data),
92 {'Content-Type' : 'x-tivo/dict-binary'}
94 result = self.__opener.open(r)
96 xml = ElementTree.parse(result).find('.')
98 if xml.findtext('state') != 'complete':
99 raise Exception(ElementTree.tostring(xml))
101 offer_id = xml.findtext('offerId')
102 content_id = offer_id.replace('of','ct')
104 self.__log('__bodyOfferModify\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
105 return offer_id, content_id
108 def __subscribe(self, offer_id, content_id, tsn):
109 data = {
110 'bodyId' : 'tsn:' + tsn,
111 'idSetSource' : {
112 'contentId': content_id,
113 'offerId' : offer_id,
114 'type' : 'singleOfferSource',
116 'title' : 'pcBodySubscription',
117 'uiType' : 'cds',
120 r = urllib2.Request(
121 'https://mind.tivo.com:8181/mind/mind7?type=subscribe&bodyId=tsn:' + tsn,
122 dictcode(data),
123 {'Content-Type' : 'x-tivo/dict-binary'}
125 result = self.__opener.open(r)
127 xml = ElementTree.parse(result).find('.')
129 self.__log('__subscribe\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
131 return xml
133 def __pcBodySearch(self):
135 data = {}
136 r = urllib2.Request(
137 'https://mind.tivo.com:8181/mind/mind7?type=pcBodySearch',
138 dictcode(data),
139 {'Content-Type' : 'x-tivo/dict-binary'}
141 result = self.__opener.open(r)
143 xml = ElementTree.parse(result).find('.')
146 self.__log('__pcBodySearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
148 return [id.text for id in xml.findall('pcBody/pcBodyId')]
150 def __collectionIdSearch(self, url):
152 data = {'url' : url}
153 r = urllib2.Request(
154 'https://mind.tivo.com:8181/mind/mind7?type=collectionIdSearch',
155 dictcode(data),
156 {'Content-Type' : 'x-tivo/dict-binary'}
158 result = self.__opener.open(r)
160 xml = ElementTree.parse( result ).find('.')
161 collection_id = xml.findtext('collectionId')
163 self.__log('__collectionIdSearch\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
165 return collection_id
167 def __pcBodyStore(self, name, replace=False):
169 data = {
170 'name' : name,
171 'replaceExisting' : str(replace).lower(),
174 r = urllib2.Request(
175 'https://mind.tivo.com:8181/mind/mind7?type=pcBodyStore',
176 dictcode(data),
177 {'Content-Type' : 'x-tivo/dict-binary'}
179 result = self.__opener.open(r)
181 xml = ElementTree.parse(result).find('.')
183 self.__log('__pcBodySearch\n%s\n\n%s' % (data, ElementTree.tostring(xml)))
185 return xml
188 def dictcode(d):
189 """Helper to create x-tivo/dict-binary"""
190 output = []
192 keys = [str(k) for k in d]
193 keys.sort()
195 for k in keys:
196 v = d[k]
198 l = len(k) | 128
199 output.append( struct.pack('>B', l) )
200 output.append( k )
202 if isinstance(v, dict):
203 output.append( struct.pack('>B', 0x02) )
204 output.append( dictcode(v) )
206 else:
207 v = str(v)
208 output.append( struct.pack('>B', 0x01) )
209 l = len(v) | 128
210 output.append( struct.pack('>B', l) )
211 output.append( v )
213 output.append( struct.pack('>B', 0x00) )
215 output.append( struct.pack('>B', 0x80) )
217 return ''.join(output)
220 if __name__ == '__main__':
221 username = 'armooo@armooo.net'
222 password = 'in(into)'
223 tsn = '6520001802C0F2A'
224 url = 'http://10.0.1.52:9032/Steph%27s%20Videos/Weekend%20%28Godard%201967%29.avi'
226 mind = Mind(username, password, True)
227 mind.pushVideo(tsn, url)