Rework help section of admin
[pyTivo/krkeegan.git] / plugins / admin / admin.py
blobf2b9c0d2fa53fbf118fbc1e619c359541cc62f17
1 import os, socket, re, sys, ConfigParser, config, time
2 import urllib2, cookielib, thread, buildhelp
3 from xml.dom import minidom
4 from ConfigParser import NoOptionError
5 from Cheetah.Template import Template
6 from plugin import Plugin
7 from urllib import unquote_plus, quote, unquote
8 from urlparse import urlparse
9 from xml.sax.saxutils import escape
10 from lrucache import LRUCache
11 import debug
13 SCRIPTDIR = os.path.dirname(__file__)
15 CLASS_NAME = 'Admin'
17 p = os.path.dirname(__file__)
18 p = p.split(os.path.sep)
19 p.pop()
20 p.pop()
21 p = os.path.sep.join(p)
22 config_file_path = os.path.join(p, 'pyTivo.conf')
24 status = {} #Global variable to control download threads
25 tivo_cache = {} #Cache of TiVo NPL
27 class Admin(Plugin):
28 CONTENT_TYPE = 'text/html'
30 def Reset(self, handler, query):
31 config.reset()
32 handler.server.reset()
33 if 'last_page' in query:
34 last_page = query['last_page'][0]
35 else:
36 last_page = 'Admin'
38 subcname = query['Container'][0]
39 cname = subcname.split('/')[0]
40 handler.send_response(200)
41 handler.end_headers()
42 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
43 t.container = cname
44 t.time = '3'
45 t.url = '/TiVoConnect?Command='+ last_page +'&Container=' + cname
46 t.text = '<h3>The pyTivo Server has been soft reset.</h3> <br>pyTivo has reloaded the pyTivo.conf'+\
47 'file and all changed should now be in effect. <br> The'+ \
48 '<a href="/TiVoConnect?Command='+ last_page +'&Container='+ cname +'"> previous</a> page will reload in 3 seconds.'
49 handler.wfile.write(t)
50 debug.debug_write(__name__, debug.fn_attr(), ['The pyTivo Server has been soft reset.'])
51 debug.print_conf(__name__, debug.fn_attr())
53 def Admin(self, handler, query):
54 #Read config file new each time in case there was any outside edits
55 config = ConfigParser.ConfigParser()
56 config.read(config_file_path)
58 shares_data = []
59 for section in config.sections():
60 if not(section.startswith('_tivo_') or section.startswith('Server')):
61 if not(config.has_option(section,'type')):
62 shares_data.append((section, dict(config.items(section, raw=True))))
63 elif config.get(section,'type').lower() != 'admin':
64 shares_data.append((section, dict(config.items(section, raw=True))))
66 subcname = query['Container'][0]
67 cname = subcname.split('/')[0]
68 handler.send_response(200)
69 handler.end_headers()
70 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'settings.tmpl'))
71 t.container = cname
72 t.server_data = dict(config.items('Server', raw=True))
73 t.server_known = buildhelp.getknown('server')
74 t.shares_data = shares_data
75 t.shares_known = buildhelp.getknown('shares')
76 t.tivos_data = [ (section, dict(config.items(section, raw=True))) for section in config.sections() \
77 if section.startswith('_tivo_')]
78 t.tivos_known = buildhelp.getknown('tivos')
79 t.help_list = buildhelp.gethelp()
80 handler.wfile.write(t)
82 def UpdateSettings(self, handler, query):
83 config = ConfigParser.ConfigParser()
84 config.read(config_file_path)
85 for key in query:
86 if key.startswith('Server.'):
87 section, option = key.split('.')
88 if option == "new__setting":
89 new_setting = query[key][0]
90 continue
91 if option == "new__value":
92 new_value = query[key][0]
93 continue
94 if query[key][0] == " ":
95 config.remove_option(section, option)
96 else:
97 config.set(section, option, query[key][0])
98 if not(new_setting == ' ' and new_value == ' '):
99 config.set('Server', new_setting, new_value)
101 sections = query['Section_Map'][0].split(']')
102 sections.pop() #last item is junk
103 for section in sections:
104 ID, name = section.split('|')
105 if query[ID][0] == "Delete_Me":
106 config.remove_section(name)
107 continue
108 if query[ID][0] != name:
109 config.remove_section(name)
110 config.add_section(query[ID][0])
111 for key in query:
112 if key.startswith(ID + '.'):
113 junk, option = key.split('.')
114 if option == "new__setting":
115 new_setting = query[key][0]
116 continue
117 if option == "new__value":
118 new_value = query[key][0]
119 continue
120 if query[key][0] == " ":
121 config.remove_option(query[ID][0], option)
122 else:
123 config.set(query[ID][0], option, query[key][0])
124 if not(new_setting == ' ' and new_value == ' '):
125 config.set(query[ID][0], new_setting, new_value)
126 if query['new_Section'][0] != " ":
127 config.add_section(query['new_Section'][0])
128 f = open(config_file_path, "w")
129 config.write(f)
130 f.close()
132 subcname = query['Container'][0]
133 cname = subcname.split('/')[0]
134 handler.send_response(200)
135 handler.end_headers()
136 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
137 t.container = cname
138 t.time = '10'
139 t.url = '/TiVoConnect?Command=Admin&Container=' + cname
140 t.text = '<h3>Your Settings have been saved.</h3> <br>You settings have been saved to the pyTivo.conf file.'+\
141 'However you will need to do a <b>Soft Reset</b> before these changes will take effect.'+\
142 '<br> The <a href="/TiVoConnect?Command=Admin&Container='+ cname +'"> Admin</a> page will reload in 10 seconds.'
143 handler.wfile.write(t)
145 def NPL(self, handler, query):
146 subcname = query['Container'][0]
147 cname = subcname.split('/')[0]
148 folder = ''
149 for name, data in config.getShares():
150 if cname == name:
151 if 'tivo_mak' in data:
152 tivo_mak = data['tivo_mak']
153 else:
154 tivo_mak = ""
155 if 'togo_path' in data:
156 togo_path = data['togo_path']
157 else:
158 togo_path = ""
160 if 'TiVo' in query:
161 tivoIP = query['TiVo'][0]
162 theurl = 'https://'+ tivoIP +'/TiVoConnect?Command=QueryContainer&Container=/NowPlaying'
163 if 'Folder' in query:
164 folder += str(query['Folder'][0])
165 theurl += '/' + folder
167 password = tivo_mak #TiVo MAK
169 r=urllib2.Request(theurl)
170 auth_handler = urllib2.HTTPDigestAuthHandler()
171 auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', password)
172 opener = urllib2.build_opener(auth_handler)
173 urllib2.install_opener(opener)
175 if theurl in tivo_cache: #check to see if we have accessed this page before
176 if tivo_cache[theurl]['thepage'] == '' or (time.time() - tivo_cache[theurl]['thepage_time']) >= 60: #if page is empty or old then retreive it
177 try:
178 handle = urllib2.urlopen(r)
179 except IOError, e:
180 handler.send_response(200)
181 handler.end_headers()
182 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
183 t.container = cname
184 t.time = '20'
185 t.url = '/TiVoConnect?Command=NPL&Container=' + cname
186 t.text = '<h3>Unable to Connect to TiVo.</h3> <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
187 '<br>This most likely caused by an incorrect Media Access Key. Please return to the ToGo page and double check your Media Access Key.' +\
188 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
189 handler.wfile.write(t)
190 return
191 tivo_cache[theurl]['thepage'] = handle.read()
192 tivo_cache[theurl]['thepage_time'] = time.time()
193 else: #not in cache
194 try:
195 handle = urllib2.urlopen(r)
196 except IOError, e:
197 handler.send_response(200)
198 handler.end_headers()
199 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
200 t.container = cname
201 t.time = '20'
202 t.url = '/TiVoConnect?Command=NPL&Container=' + cname
203 t.text = '<h3>Unable to Connect to TiVo.</h3> <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
204 '<br>This most likely caused by an incorrect Media Access Key. Please return to the ToGo page and double check your Media Access Key.' +\
205 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
206 handler.wfile.write(t)
207 return
208 tivo_cache[theurl] = {}
209 tivo_cache[theurl]['thepage'] = handle.read()
210 tivo_cache[theurl]['thepage_time'] = time.time()
212 xmldoc = minidom.parseString(tivo_cache[theurl]['thepage'])
213 items = xmldoc.getElementsByTagName('Item')
215 data = []
216 for item in items:
217 entry = {}
218 entry['Title'] = item.getElementsByTagName("Title")[0].firstChild.data
219 entry['ContentType'] = item.getElementsByTagName("ContentType")[0].firstChild.data
220 if (len(item.getElementsByTagName("UniqueId")) >= 1):
221 entry['UniqueId'] = item.getElementsByTagName("UniqueId")[0].firstChild.data
222 if entry['ContentType'] == 'x-tivo-container/folder':
223 entry['TotalItems'] = item.getElementsByTagName("TotalItems")[0].firstChild.data
224 entry['LastChangeDate'] = item.getElementsByTagName("LastChangeDate")[0].firstChild.data
225 entry['LastChangeDate'] = time.strftime("%b %d, %Y", time.localtime(int(entry['LastChangeDate'], 16)))
226 else:
227 link = item.getElementsByTagName("Links")[0]
228 if (len(link.getElementsByTagName("CustomIcon")) >= 1):
229 entry['Icon'] = link.getElementsByTagName("CustomIcon")[0].getElementsByTagName("Url")[0].firstChild.data
230 if (len(link.getElementsByTagName("Content")) >= 1):
231 entry['Url'] = link.getElementsByTagName("Content")[0].getElementsByTagName("Url")[0].firstChild.data
232 parse_url = urlparse(entry['Url'])
233 entry['Url'] = quote('http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4])
234 keys = ['SourceSize', 'Duration', 'CaptureDate', 'EpisodeTitle', 'Description', 'SourceChannel', 'SourceStation']
235 for key in keys:
236 try:
237 entry[key] = item.getElementsByTagName(key)[0].firstChild.data
238 except:
239 entry[key] = ''
240 entry['SourceSize'] = "%.3f GB" % float(float(entry['SourceSize'])/(1024*1024*1024))
241 entry['Duration'] = str(int(entry['Duration'])/(60*60*1000)).zfill(2) + ':' \
242 + str((int(entry['Duration'])%(60*60*1000))/(60*1000)).zfill(2) + ':' \
243 + str((int(entry['Duration'])/1000)%60).zfill(2)
244 entry['CaptureDate'] = time.strftime("%b %d, %Y", time.localtime(int(entry['CaptureDate'], 16)))
246 data.append(entry)
247 else:
248 data = []
249 tivoIP = ''
251 subcname = query['Container'][0]
252 cname = subcname.split('/')[0]
253 handler.send_response(200)
254 handler.end_headers()
255 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'npl.tmpl'))
256 t.folder = folder
257 t.status = status
258 t.tivo_mak = tivo_mak
259 t.togo_path = togo_path
260 t.tivos = handler.tivos
261 t.tivoIP = tivoIP
262 t.container = cname
263 t.data = data
264 t.unquote = unquote
265 t.len = len
266 handler.wfile.write(t)
268 def get_tivo_file(self, url, mak, tivoIP, outfile):
269 #global status
270 cj = cookielib.LWPCookieJar()
272 r=urllib2.Request(url)
273 auth_handler = urllib2.HTTPDigestAuthHandler()
274 auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', mak)
275 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj), auth_handler)
276 urllib2.install_opener(opener)
278 try:
279 handle = urllib2.urlopen(r)
280 except IOError, e:
281 #If we get "Too many transfers error" try a second time. For some reason
282 #urllib2 does not properly close connections when a transfer is canceled.
283 if e.code == 503:
284 try:
285 handle = urllib2.urlopen(r)
286 except IOError, e:
287 status[url]['running'] = False
288 status[url]['error'] = e.code
289 return
290 else:
291 status[url]['running'] = False
292 status[url]['error'] = e.code
293 return
295 f = open(outfile, 'wb')
296 kilobytes = 0
297 start_time = time.time()
298 output = handle.read(1024)
299 while status[url]['running'] and output != '':
300 kilobytes += 1
301 f.write(output)
302 if ((time.time() - start_time) >= 5):
303 status[url]['rate'] = int(kilobytes/(time.time() - start_time))
304 kilobytes = 0
305 start_time = time.time()
306 output = handle.read(1024)
307 status[url]['running'] = False
308 handle.close()
309 f.close()
310 return
312 def ToGo(self, handler, query):
313 subcname = query['Container'][0]
314 cname = subcname.split('/')[0]
315 for name, data in config.getShares():
316 if cname == name:
317 if 'tivo_mak' in data:
318 tivo_mak = data['tivo_mak']
319 else:
320 tivo_mak = ""
321 if 'togo_path' in data:
322 togo_path = data['togo_path']
323 else:
324 togo_path = ""
325 folder = ''
326 if 'Folder' in query:
327 folder += str(query['Folder'][0])
328 if tivo_mak != "" and togo_path != "":
329 parse_url = urlparse(str(query['Url'][0]))
330 theurl = 'http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4]
331 password = tivo_mak #TiVo MAK
332 tivoIP = query['TiVo'][0]
333 name = unquote(parse_url[2])[10:300].split('.')
334 name.insert(-1," - " + unquote(parse_url[4]).split("id=")[1] + ".")
335 outfile = os.path.join(togo_path, "".join(name))
337 status[theurl] = {'running':True, 'error':'', 'rate':'', 'finished':False}
339 thread.start_new_thread(Admin.get_tivo_file, (self, theurl, password, tivoIP, outfile))
341 handler.send_response(200)
342 handler.end_headers()
343 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
344 t.container = cname
345 t.time = '3'
346 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder
347 t.text = '<h3>Transfer Initiated.</h3> <br>You selected transfer has been initiated.'+\
348 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder +'"> ToGo</a> page will reload in 3 seconds.'
349 handler.wfile.write(t)
350 else:
351 handler.send_response(200)
352 handler.end_headers()
353 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
354 t.container = cname
355 t.time = '10'
356 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder
357 t.text = '<h3>Missing Data.</h3> <br>You must set both "tivo_mak" and "togo_path" before using this function.'+\
358 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder +'"> ToGo</a> page will reload in 10 seconds.'
359 handler.wfile.write(t)
361 def ToGoStop(self, handler, query):
362 parse_url = urlparse(str(query['Url'][0]))
363 theurl = 'http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4]
364 folder = ''
365 if 'Folder' in query:
366 folder += str(query['Folder'][0])
368 status[theurl]['running'] = False
370 subcname = query['Container'][0]
371 cname = subcname.split('/')[0]
372 handler.send_response(200)
373 handler.end_headers()
374 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
375 t.container = cname
376 t.time = '3'
377 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder
378 t.text = '<h3>Transfer Stopped.</h3> <br>Your transfer has been stopped.'+\
379 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + folder +'"> ToGo</a> page will reload in 3 seconds.'
380 handler.wfile.write(t)
383 def SaveNPL(self, handler, query):
384 config = ConfigParser.ConfigParser()
385 config.read(config_file_path)
386 if 'tivo_mak' in query:
387 config.set(query['Container'][0], 'tivo_mak', query['tivo_mak'][0])
388 if 'togo_path' in query:
389 config.set(query['Container'][0], 'togo_path', query['togo_path'][0])
390 f = open(config_file_path, "w")
391 config.write(f)
392 f.close()
394 subcname = query['Container'][0]
395 cname = subcname.split('/')[0]
396 handler.send_response(200)
397 handler.end_headers()
398 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
399 t.container = cname
400 t.time = '2'
401 t.url = '/TiVoConnect?last_page=NPL&Command=Reset&Container=' + cname
402 t.text = '<h3>Your Settings have been saved.</h3> <br>You settings have been saved to the pyTivo.conf file.'+\
403 'pyTivo will now do a <b>Soft Reset</b> to allow these changes to take effect.'+\
404 '<br> The <a href="/TiVoConnect?last_page=NPL&Command=Reset&Container='+ cname +'"> Reset</a> will occur in 2 seconds.'
405 handler.wfile.write(t)