Made the push page look more like the ToGo page; darker alternate color
[pyTivo/wmcbrine.git] / httpserver.py
blobf7661429faa10194e8486ac63a7bad8988d507b2
1 import BaseHTTPServer
2 import SocketServer
3 import cgi
4 import logging
5 import os
6 import re
7 import socket
8 import time
9 from urllib import unquote_plus, quote, unquote
10 from urlparse import urlparse
11 from xml.sax.saxutils import escape
13 from Cheetah.Template import Template
14 import config
15 from plugin import GetPlugin
17 SCRIPTDIR = os.path.dirname(__file__)
19 VIDEO_FORMATS = """<?xml version="1.0" encoding="utf-8"?>
20 <TiVoFormats><Format>
21 <ContentType>video/x-tivo-mpeg</ContentType><Description/>
22 </Format></TiVoFormats>"""
24 class TivoHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
25 containers = {}
27 def __init__(self, server_address, RequestHandlerClass):
28 BaseHTTPServer.HTTPServer.__init__(self, server_address,
29 RequestHandlerClass)
30 self.daemon_threads = True
32 def add_container(self, name, settings):
33 if name in self.containers or name == 'TiVoConnect':
34 raise "Container Name in use"
35 try:
36 settings['content_type'] = GetPlugin(settings['type']).CONTENT_TYPE
37 self.containers[name] = settings
38 except KeyError:
39 print 'Unable to add container', name
41 def reset(self):
42 self.containers.clear()
43 for section, settings in config.getShares():
44 self.add_container(section, settings)
46 def set_beacon(self, beacon):
47 self.beacon = beacon
49 class TivoHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
50 tivos = {}
51 tivo_names = {}
53 def address_string(self):
54 host, port = self.client_address[:2]
55 return host
57 def do_GET(self):
58 tsn = self.headers.getheader('TiVo_TCD_ID',
59 self.headers.getheader('tsn', ''))
60 if tsn:
61 ip = self.address_string()
62 self.tivos[tsn] = ip
64 if not tsn in self.tivo_names:
65 self.tivo_names[tsn] = self.server.beacon.get_name(ip)
67 basepath = unquote_plus(self.path).split('/')[1]
69 ## Get File
70 for name, container in self.server.containers.items():
71 if basepath == name:
72 plugin = GetPlugin(container['type'])
73 plugin.send_file(self, container, name)
74 return
76 ## Not a file not a TiVo command
77 if not self.path.startswith('/TiVoConnect'):
78 self.infopage()
79 return
81 o = urlparse("http://fake.host" + self.path)
82 query = cgi.parse_qs(o[4])
84 self.handle_query(query)
86 def do_POST(self):
87 ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
88 if ctype == 'multipart/form-data':
89 query = cgi.parse_multipart(self.rfile, pdict)
90 else:
91 length = int(self.headers.getheader('content-length'))
92 qs = self.rfile.read(length)
93 query = cgi.parse_qs(qs, keep_blank_values=1)
94 self.handle_query(query)
96 def handle_query(self, query):
97 mname = False
98 if 'Command' in query and len(query['Command']) >= 1:
100 command = query['Command'][0]
102 # If we are looking at the root container
103 if (command == 'QueryContainer' and
104 (not 'Container' in query or query['Container'][0] == '/')):
105 self.root_container()
106 return
108 if 'Container' in query:
109 # Dispatch to the container plugin
110 basepath = unquote(query['Container'][0].split('/')[0])
111 for name, container in self.server.containers.items():
112 if basepath == name:
113 plugin = GetPlugin(container['type'])
114 if hasattr(plugin, command):
115 method = getattr(plugin, command)
116 method(self, query)
117 return
118 else:
119 break
121 elif (command == 'QueryFormats' and 'SourceFormat' in query and
122 query['SourceFormat'][0].startswith('video')):
123 self.send_response(200)
124 self.end_headers()
125 self.wfile.write(VIDEO_FORMATS)
126 return
128 # If we made it here it means we couldn't match the request to
129 # anything.
130 self.unsupported(query)
132 def root_container(self):
133 tsn = self.headers.getheader('TiVo_TCD_ID', '')
134 tsnshares = config.getShares(tsn)
135 tsncontainers = {}
136 for section, settings in tsnshares:
137 try:
138 settings['content_type'] = \
139 GetPlugin(settings['type']).CONTENT_TYPE
140 tsncontainers[section] = settings
141 except Exception, msg:
142 print section, '-', msg
143 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
144 'root_container.tmpl'))
145 t.containers = tsncontainers
146 t.hostname = socket.gethostname()
147 t.escape = escape
148 t.quote = quote
149 self.send_response(200)
150 self.end_headers()
151 self.wfile.write(t)
153 def infopage(self):
154 self.send_response(200)
155 self.send_header('Content-type', 'text/html')
156 self.end_headers()
157 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
158 'info_page.tmpl'))
159 t.admin = ''
160 for section, settings in config.getShares():
161 if 'type' in settings and settings['type'] == 'admin':
162 t.admin += ('<a href="/TiVoConnect?Command=Admin&Container=' +
163 quote(section) +
164 '">pyTivo Web Configuration</a><br>' +
165 '<a href="/TiVoConnect?Command=NPL&Container=' +
166 quote(section) + '">pyTivo ToGo</a><br>')
167 if t.admin == '':
168 t.admin = ('<br><b>No Admin plugin installed in pyTivo.conf</b>' +
169 '<br> If you wish to use the admin plugin add the ' +
170 'following lines to pyTivo.conf<br><br>' +
171 '[Admin]<br>type=admin')
173 t.shares = 'Video shares:<br/>'
174 for section, settings in config.getShares():
175 if settings.get('type') == 'video':
176 t.shares += ('<a href="TiVoConnect?Command=QueryContainer&' +
177 'Container=' + quote(section) + '">' + section +
178 '</a><br/>')
180 self.wfile.write(t)
182 def unsupported(self, query):
183 self.send_response(404)
184 self.send_header('Content-type', 'text/html')
185 self.end_headers()
186 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
187 'unsupported.tmpl'))
188 t.query = query
189 self.wfile.write(t)
191 if __name__ == '__main__':
192 def start_server():
193 httpd = TivoHTTPServer(('', 9032), TivoHTTPHandler)
194 httpd.add_container('test', 'x-container/tivo-videos',
195 r'C:\Documents and Settings\Armooo' +
196 r'\Desktop\pyTivo\test')
197 httpd.serve_forever()
199 start_server()