8 from urllib
import quote
, unquote
9 from xml
.dom
import minidom
11 from Cheetah
.Template
import Template
14 from metadata
import tag_data
, from_container
15 from plugin
import EncodeUnicode
, Plugin
17 SCRIPTDIR
= os
.path
.dirname(__file__
)
21 # Some error/status message templates
23 MISSING
= """<h3>Missing Data.</h3> <br>
24 You must set both "tivo_mak" and "togo_path" before using this
26 The <a href="/TiVoConnect?Command=%s&Container=%s&TiVo=%s">ToGo</a> page
27 will reload in 10 seconds."""
29 RESET_MSG
= """<h3>The pyTivo Server has been soft reset.</h3> <br>
30 pyTivo has reloaded the pyTivo.conf file and all changes should now be
32 The <a href="/TiVoConnect?Command=%s&Container=%s">previous</a> page
33 will reload in 3 seconds."""
35 TRANS_INIT
= """<h3>Transfer Initiated.</h3> <br>
36 Your selected transfer has been initiated.<br>
37 The <a href="/TiVoConnect?Command=%s&Container=%s&TiVo=%s">ToGo</a> page
38 will reload in 3 seconds."""
40 TRANS_STOP
= """<h3>Transfer Stopped.</h3> <br>
41 Your transfer has been stopped.<br>
42 The <a href="/TiVoConnect?Command=%s&Container=%s&TiVo=%s">ToGo</a> page
43 will reload in 3 seconds."""
45 UNABLE
= """<h3>Unable to Connect to TiVo.</h3> <br>
46 pyTivo was unable to connect to the TiVo at %s</br>
47 This most likely caused by an incorrect Media Access Key. Please return
48 to the ToGo page and double check your Media Access Key.<br>
49 The <a href="/TiVoConnect?Command=NPL&Container=%s">ToGo</a> page will
50 reload in 20 seconds."""
52 # Preload the templates
53 trname
= os
.path
.join(SCRIPTDIR
, 'templates', 'redirect.tmpl')
54 tnname
= os
.path
.join(SCRIPTDIR
, 'templates', 'npl.tmpl')
55 REDIRECT_TEMPLATE
= file(trname
, 'rb').read()
56 NPL_TEMPLATE
= file(tnname
, 'rb').read()
58 status
= {} # Global variable to control download threads
59 tivo_cache
= {} # Cache of TiVo NPL
62 CONTENT_TYPE
= 'text/html'
64 def NPL(self
, handler
, query
):
65 shows_per_page
= 50 # Change this to alter the number of shows returned
66 cname
= query
['Container'][0].split('/')[0]
68 tivo_mak
= config
.get_server('tivo_mak')
69 togo_path
= config
.get_server('togo_path')
70 for name
, data
in config
.getShares():
72 togo_path
= data
.get('path')
75 tivoIP
= query
['TiVo'][0]
76 theurl
= ('https://' + tivoIP
+
77 '/TiVoConnect?Command=QueryContainer&ItemCount=' +
78 str(shows_per_page
) + '&Container=/NowPlaying')
80 folder
+= query
['Folder'][0]
81 theurl
+= '/' + folder
82 if 'AnchorItem' in query
:
83 theurl
+= '&AnchorItem=' + quote(query
['AnchorItem'][0])
84 if 'AnchorOffset' in query
:
85 theurl
+= '&AnchorOffset=' + query
['AnchorOffset'][0]
87 r
= urllib2
.Request(theurl
)
88 auth_handler
= urllib2
.HTTPDigestAuthHandler()
89 auth_handler
.add_password('TiVo DVR', tivoIP
, 'tivo', tivo_mak
)
90 opener
= urllib2
.build_opener(auth_handler
)
91 urllib2
.install_opener(opener
)
93 if (theurl
not in tivo_cache
or
94 (time
.time() - tivo_cache
[theurl
]['thepage_time']) >= 60):
95 # if page is not cached or old then retreive it
97 page
= urllib2
.urlopen(r
)
99 t
= Template(REDIRECT_TEMPLATE
)
101 t
.url
= '/TiVoConnect?Command=NPL&Container=' + quote(cname
)
102 t
.text
= UNABLE
% (tivoIP
, quote(cname
))
103 handler
.send_response(200)
104 handler
.end_headers()
105 handler
.wfile
.write(t
)
107 tivo_cache
[theurl
] = {'thepage': minidom
.parse(page
),
108 'thepage_time': time
.time()}
111 xmldoc
= tivo_cache
[theurl
]['thepage']
112 items
= xmldoc
.getElementsByTagName('Item')
113 TotalItems
= tag_data(xmldoc
, 'Details/TotalItems')
114 ItemStart
= tag_data(xmldoc
, 'ItemStart')
115 ItemCount
= tag_data(xmldoc
, 'ItemCount')
116 FirstAnchor
= tag_data(items
[0], 'Links/Content/Url')
121 entry
['ContentType'] = tag_data(item
, 'ContentType')
122 for tag
in ('CopyProtected', 'UniqueId'):
123 value
= tag_data(item
, tag
)
126 if entry
['ContentType'] == 'x-tivo-container/folder':
127 entry
['Title'] = tag_data(item
, 'Title')
128 entry
['TotalItems'] = tag_data(item
, 'TotalItems')
129 lc
= int(tag_data(item
, 'LastChangeDate'), 16)
130 entry
['LastChangeDate'] = time
.strftime('%b %d, %Y',
133 entry
.update(from_container(item
))
134 keys
= {'Icon': 'Links/CustomIcon/Url',
135 'Url': 'Links/Content/Url',
136 'SourceSize': 'Details/SourceSize',
137 'Duration': 'Details/Duration',
138 'CaptureDate': 'Details/CaptureDate'}
140 value
= tag_data(item
, keys
[key
])
144 entry
['SourceSize'] = ( '%.3f GB' %
145 (float(entry
['SourceSize']) / (1024 ** 3)) )
147 dur
= int(entry
['Duration']) / 1000
148 entry
['Duration'] = ( '%02d:%02d:%02d' %
149 (dur
/ 3600, (dur
% 3600) / 60, dur
% 60) )
151 entry
['CaptureDate'] = time
.strftime('%b %d, %Y',
152 time
.localtime(int(entry
['CaptureDate'], 16)))
163 cname
= query
['Container'][0].split('/')[0]
164 t
= Template(NPL_TEMPLATE
, filter=EncodeUnicode
)
168 t
.tivo_mak
= tivo_mak
169 t
.togo_path
= togo_path
170 t
.tivos
= config
.tivos
171 t
.tivo_names
= config
.tivo_names
176 t
.TotalItems
= int(TotalItems
)
177 t
.ItemStart
= int(ItemStart
)
178 t
.ItemCount
= int(ItemCount
)
179 t
.FirstAnchor
= quote(FirstAnchor
)
180 t
.shows_per_page
= shows_per_page
181 handler
.send_response(200)
182 handler
.send_header('Content-Type', 'text/html')
183 handler
.end_headers()
184 handler
.wfile
.write(t
)
186 def get_tivo_file(self
, url
, mak
, togo_path
):
188 cj
= cookielib
.LWPCookieJar()
190 parse_url
= urlparse
.urlparse(url
)
192 name
= unquote(parse_url
[2])[10:].split('.')
193 name
.insert(-1," - " + unquote(parse_url
[4]).split("id=")[1] + ".")
194 outfile
= os
.path
.join(togo_path
, "".join(name
))
196 r
= urllib2
.Request(url
)
197 auth_handler
= urllib2
.HTTPDigestAuthHandler()
198 auth_handler
.add_password('TiVo DVR', parse_url
[1], 'tivo', mak
)
199 opener
= urllib2
.build_opener(urllib2
.HTTPCookieProcessor(cj
),
201 urllib2
.install_opener(opener
)
204 handle
= urllib2
.urlopen(r
)
206 status
[url
]['running'] = False
207 status
[url
]['error'] = e
.code
210 f
= open(outfile
, 'wb')
212 start_time
= time
.time()
214 while status
[url
]['running']:
215 output
= handle
.read(1024000)
218 length
+= len(output
)
221 elapsed
= now
- start_time
223 status
[url
]['rate'] = int(length
/ elapsed
) / 1024
224 status
[url
]['size'] += length
227 if status
[url
]['running']:
228 status
[url
]['finished'] = True
229 except Exception, msg
:
230 logging
.getLogger('pyTivo.togo').info(msg
)
231 status
[url
]['running'] = False
235 def ToGo(self
, handler
, query
):
236 cname
= query
['Container'][0].split('/')[0]
237 tivoIP
= query
['TiVo'][0]
238 tivo_mak
= config
.get_server('tivo_mak')
239 togo_path
= config
.get_server('togo_path')
240 for name
, data
in config
.getShares():
241 if togo_path
== name
:
242 togo_path
= data
.get('path')
243 t
= Template(REDIRECT_TEMPLATE
)
244 command
= query
['Redirect'][0]
245 params
= (command
, quote(cname
), tivoIP
)
246 if tivo_mak
and togo_path
:
247 theurl
= query
['Url'][0]
248 status
[theurl
] = {'running': True, 'error': '', 'rate': '',
249 'size': 0, 'finished': False}
250 thread
.start_new_thread(ToGo
.get_tivo_file
,
251 (self
, theurl
, tivo_mak
, togo_path
))
253 t
.text
= TRANS_INIT
% params
256 t
.text
= MISSING
% params
257 t
.url
= ('/TiVoConnect?Command=' + command
+ '&Container=' +
258 quote(cname
) + '&TiVo=' + tivoIP
)
259 handler
.send_response(200)
260 handler
.end_headers()
261 handler
.wfile
.write(t
)
263 def ToGoStop(self
, handler
, query
):
264 theurl
= query
['Url'][0]
265 status
[theurl
]['running'] = False
267 cname
= query
['Container'][0].split('/')[0]
268 tivoIP
= query
['TiVo'][0]
269 command
= query
['Redirect'][0]
270 t
= Template(REDIRECT_TEMPLATE
)
272 t
.url
= ('/TiVoConnect?Command=' + command
+ '&Container=' +
273 quote(cname
) + '&TiVo=' + tivoIP
)
274 t
.text
= TRANS_STOP
% (command
, quote(cname
), tivoIP
)
275 handler
.send_response(200)
276 handler
.end_headers()
277 handler
.wfile
.write(t
)