We were never setting the MIME type for video transfers; and, if set to
[pyTivo/wmcbrine.git] / httpserver.py
blob5ee9289ecc83f270ee4f9b260e667d14b0d49030
1 import BaseHTTPServer
2 import SocketServer
3 import logging
4 import os
5 import re
6 import socket
7 import time
8 from cgi import parse_qs
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 fuck them
77 if not self.path.startswith('/TiVoConnect'):
78 self.infopage()
79 return
81 o = urlparse("http://fake.host" + self.path)
82 query = parse_qs(o[4])
84 mname = False
85 if 'Command' in query and len(query['Command']) >= 1:
87 command = query['Command'][0]
89 # If we are looking at the root container
90 if (command == 'QueryContainer' and
91 (not 'Container' in query or query['Container'][0] == '/')):
92 self.root_container()
93 return
95 if 'Container' in query:
96 # Dispatch to the container plugin
97 basepath = unquote(query['Container'][0].split('/')[0])
98 for name, container in self.server.containers.items():
99 if basepath == name:
100 plugin = GetPlugin(container['type'])
101 if hasattr(plugin, command):
102 method = getattr(plugin, command)
103 method(self, query)
104 return
105 else:
106 break
108 elif (command == 'QueryFormats' and 'SourceFormat' in query and
109 query['SourceFormat'][0].startswith('video')):
110 self.send_response(200)
111 self.end_headers()
112 self.wfile.write(VIDEO_FORMATS)
113 return
115 # If we made it here it means we couldn't match the request to
116 # anything.
117 self.unsupported(query)
119 def root_container(self):
120 tsn = self.headers.getheader('TiVo_TCD_ID', '')
121 tsnshares = config.getShares(tsn)
122 tsncontainers = {}
123 for section, settings in tsnshares:
124 try:
125 settings['content_type'] = \
126 GetPlugin(settings['type']).CONTENT_TYPE
127 tsncontainers[section] = settings
128 except Exception, msg:
129 print section, '-', msg
130 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
131 'root_container.tmpl'))
132 t.containers = tsncontainers
133 t.hostname = socket.gethostname()
134 t.escape = escape
135 t.quote = quote
136 self.send_response(200)
137 self.end_headers()
138 self.wfile.write(t)
140 def infopage(self):
141 self.send_response(200)
142 self.send_header('Content-type', 'text/html')
143 self.end_headers()
144 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
145 'info_page.tmpl'))
146 t.admin = ''
147 for section, settings in config.getShares():
148 if 'type' in settings and settings['type'] == 'admin':
149 t.admin += ('<a href="/TiVoConnect?Command=Admin&Container=' +
150 quote(section) +
151 '">pyTivo Web Configuration</a><br>' +
152 '<a href="/TiVoConnect?Command=NPL&Container=' +
153 quote(section) + '">pyTivo ToGo</a><br>')
154 if t.admin == '':
155 t.admin = ('<br><b>No Admin plugin installed in pyTivo.conf</b>' +
156 '<br> If you wish to use the admin plugin add the ' +
157 'following lines to pyTivo.conf<br><br>' +
158 '[Admin]<br>type=admin')
160 t.shares = 'Video shares:<br/>'
161 for section, settings in config.getShares():
162 if settings.get('type') == 'video':
163 t.shares += ('<a href="TiVoConnect?Command=QueryContainer&' +
164 'Container=' + quote(section) + '">' + section +
165 '</a><br/>')
167 self.wfile.write(t)
169 def unsupported(self, query):
170 self.send_response(404)
171 self.send_header('Content-type', 'text/html')
172 self.end_headers()
173 t = Template(file=os.path.join(SCRIPTDIR, 'templates',
174 'unsupported.tmpl'))
175 t.query = query
176 self.wfile.write(t)
178 if __name__ == '__main__':
179 def start_server():
180 httpd = TivoHTTPServer(('', 9032), TivoHTTPHandler)
181 httpd.add_container('test', 'x-container/tivo-videos',
182 r'C:\Documents and Settings\Armooo' +
183 r'\Desktop\pyTivo\test')
184 httpd.serve_forever()
186 start_server()