Parse the container type from ffmpeg output; check for appropriate
[pyTivo/wmcbrine.git] / httpserver.py
blobb2220eade1e06b07ab06cfec266f74ef898fafe9
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
31 self.logger = logging.getLogger('pyTivo')
33 def add_container(self, name, settings):
34 if name in self.containers or name == 'TiVoConnect':
35 raise "Container Name in use"
36 try:
37 settings['content_type'] = GetPlugin(settings['type']).CONTENT_TYPE
38 self.containers[name] = settings
39 except KeyError:
40 self.logger.error('Unable to add container ' + name)
42 def reset(self):
43 self.containers.clear()
44 for section, settings in config.getShares():
45 self.add_container(section, settings)
47 def handle_error(self, request, client_address):
48 self.logger.exception('Exception during request from %s' %
49 (client_address,))
51 def set_beacon(self, beacon):
52 self.beacon = beacon
54 class TivoHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
55 tivos = {}
56 tivo_names = {}
58 def __init__(self, request, client_address, server):
59 self.wbufsize = 0x10000
60 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request,
61 client_address, server)
63 def address_string(self):
64 host, port = self.client_address[:2]
65 return host
67 def do_GET(self):
68 tsn = self.headers.getheader('TiVo_TCD_ID',
69 self.headers.getheader('tsn', ''))
70 if tsn:
71 ip = self.address_string()
72 self.tivos[tsn] = ip
74 if not tsn in self.tivo_names:
75 self.tivo_names[tsn] = self.server.beacon.get_name(ip)
77 basepath = unquote_plus(self.path).split('/')[1]
79 ## Get File
80 for name, container in self.server.containers.items():
81 if basepath == name:
82 plugin = GetPlugin(container['type'])
83 plugin.send_file(self, container, name)
84 return
86 ## Not a file not a TiVo command
87 if not self.path.startswith('/TiVoConnect'):
88 self.infopage()
89 return
91 o = urlparse("http://fake.host" + self.path)
92 query = cgi.parse_qs(o[4])
94 self.handle_query(query)
96 def do_POST(self):
97 ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
98 if ctype == 'multipart/form-data':
99 query = cgi.parse_multipart(self.rfile, pdict)
100 else:
101 length = int(self.headers.getheader('content-length'))
102 qs = self.rfile.read(length)
103 query = cgi.parse_qs(qs, keep_blank_values=1)
104 self.handle_query(query)
106 def handle_query(self, query):
107 mname = False
108 if 'Command' in query and len(query['Command']) >= 1:
110 command = query['Command'][0]
112 # If we are looking at the root container
113 if (command == 'QueryContainer' and
114 (not 'Container' in query or query['Container'][0] == '/')):
115 self.root_container()
116 return
118 if 'Container' in query:
119 # Dispatch to the container plugin
120 basepath = unquote(query['Container'][0].split('/')[0])
121 for name, container in self.server.containers.items():
122 if basepath == name:
123 plugin = GetPlugin(container['type'])
124 if hasattr(plugin, command):
125 method = getattr(plugin, command)
126 method(self, query)
127 return
128 else:
129 break
131 elif (command == 'QueryFormats' and 'SourceFormat' in query and
132 query['SourceFormat'][0].startswith('video')):
133 self.send_response(200)
134 self.end_headers()
135 self.wfile.write(VIDEO_FORMATS)
136 return
138 # If we made it here it means we couldn't match the request to
139 # anything.
140 self.unsupported(query)
142 def log_message(self, format, *args):
143 self.server.logger.info("%s [%s] %s" % (self.address_string(),
144 self.log_date_time_string(), format%args))
146 def root_container(self):
147 tsn = self.headers.getheader('TiVo_TCD_ID', '')
148 tsnshares = config.getShares(tsn)
149 tsncontainers = {}
150 for section, settings in tsnshares:
151 try:
152 settings['content_type'] = \
153 GetPlugin(settings['type']).CONTENT_TYPE
154 tsncontainers[section] = settings
155 except Exception, msg:
156 self.server.logger.error(section + ' - ' + msg)
157 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
158 'root_container.tmpl'))
159 t.containers = tsncontainers
160 t.hostname = socket.gethostname()
161 t.escape = escape
162 t.quote = quote
163 self.send_response(200)
164 self.end_headers()
165 self.wfile.write(t)
167 def infopage(self):
168 self.send_response(200)
169 self.send_header('Content-type', 'text/html')
170 self.end_headers()
171 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
172 'info_page.tmpl'))
173 t.admin = ''
174 for section, settings in config.getShares():
175 if 'type' in settings and settings['type'] == 'admin':
176 t.admin += ('<a href="/TiVoConnect?Command=Admin&Container=' +
177 quote(section) +
178 '">Web Configuration</a><br>' +
179 '<a href="/TiVoConnect?Command=NPL&Container=' +
180 quote(section) + '">ToGo</a><br>')
181 if t.admin == '':
182 t.admin = ('<br><b>No Admin plugin installed in pyTivo.conf</b>' +
183 '<br> If you wish to use the admin plugin add the ' +
184 'following lines to pyTivo.conf<br><br>' +
185 '[Admin]<br>type=admin')
187 t.shares = 'Video shares:<br/>'
188 for section, settings in config.getShares():
189 if settings.get('type') == 'video':
190 t.shares += ('<a href="TiVoConnect?Command=QueryContainer&' +
191 'Container=' + quote(section) + '">' + section +
192 '</a><br/>')
194 self.wfile.write(t)
196 def unsupported(self, query):
197 self.send_response(404)
198 self.send_header('Content-type', 'text/html')
199 self.end_headers()
200 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
201 'unsupported.tmpl'))
202 t.query = query
203 self.wfile.write(t)
205 if __name__ == '__main__':
206 def start_server():
207 httpd = TivoHTTPServer(('', 9032), TivoHTTPHandler)
208 httpd.add_container('test', 'x-container/tivo-videos',
209 r'C:\Documents and Settings\Armooo' +
210 r'\Desktop\pyTivo\test')
211 httpd.serve_forever()
213 start_server()