pyTivo
[pyTivo/krkeegan.git] / httpserver.py
blobed64f8696c6a0ec827d6db90833cc688c2d58fc1
1 import time, os, BaseHTTPServer, SocketServer, socket, re
2 from urllib import unquote_plus, quote, unquote
3 from urlparse import urlparse
4 from xml.sax.saxutils import escape
5 from cgi import parse_qs
6 from Cheetah.Template import Template
7 import transcode
9 SCRIPTDIR = os.path.dirname(__file__)
11 class TivoHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
12 containers = {}
14 def __init__(self, server_address, RequestHandlerClass):
15 BaseHTTPServer.HTTPServer.__init__(self, server_address, RequestHandlerClass)
16 self.daemon_threads = True
18 def add_container(self, name, type, path):
19 if self.containers.has_key(name) or name == 'TivoConnect':
20 raise "Container Name in use"
21 self.containers[name] = {'type' : type, 'path' : path}
23 class TivoHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
24 def do_GET(self):
26 for name, container in self.server.containers.items():
27 #XXX make a regex
28 if self.path.startswith('/' + name):
29 self.send_static(name, container)
30 return
32 if not self.path.startswith('/TiVoConnect'):
33 self.infopage()
34 return
36 o = urlparse("http://fake.host" + self.path)
37 query = parse_qs(o.query)
39 mname = False
40 if query.has_key('Command') and len(query['Command']) >= 1:
41 mname = 'do_' + query['Command'][0]
42 if mname and hasattr(self, mname):
43 method = getattr(self, mname)
44 method(query)
45 else:
46 self.unsuported(query)
48 def do_QueryContainer(self, query):
50 if not query.has_key('Container'):
51 query['Container'] = ['/']
53 if query['Container'][0] == '/':
54 t = Template(file=os.path.join(SCRIPTDIR, 'templates', 'root_container.tmpl'))
55 t.containers = self.server.containers
56 t.hostname = socket.gethostname()
57 self.send_response(200)
58 self.end_headers()
59 self.wfile.write(t)
60 else:
61 subcname = query['Container'][0]
62 cname = subcname.split('/')[0]
64 if not self.server.containers.has_key(cname) or not self.get_local_path(query):
65 self.send_response(404)
66 self.end_headers()
67 return
69 path = self.get_local_path(query)
70 def isdir(file):
71 return os.path.isdir(os.path.join(path, file))
73 self.send_response(200)
74 self.end_headers()
75 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'container.tmpl'))
76 t.name = subcname
77 t.files, t.total, t.start = self.get_files(query)
78 t.isdir = isdir
79 t.quote = quote
80 t.escape = escape
81 self.wfile.write(t)
83 def get_local_path(self, query):
85 subcname = query['Container'][0]
86 container = self.server.containers[subcname.split('/')[0]]
88 path = container['path']
89 for folder in subcname.split('/')[1:]:
90 if folder == '..':
91 return False
92 path = os.path.join(path, folder)
93 return path
95 def get_files(self, query):
96 subcname = query['Container'][0]
97 path = self.get_local_path(query)
99 files = os.listdir(path)
100 files = filter(lambda f: os.path.isdir(os.path.join(path, f)) or transcode.suported_format(os.path.join(path,f)), files)
101 totalFiles = len(files)
103 def dir_sort(x, y):
104 xdir = os.path.isdir(os.path.join(path, x))
105 ydir = os.path.isdir(os.path.join(path, y))
107 if xdir and ydir:
108 return name_sort(x, y)
109 elif xdir:
110 return -1
111 elif ydir:
112 return 1
113 else:
114 return name_sort(x, y)
116 def name_sort(x, y):
117 numbername = re.compile(r'(\d*)(.*)')
118 m = numbername.match(x)
119 xNumber = m.group(1)
120 xStr = m.group(2)
121 m = numbername.match(y)
122 yNumber = m.group(1)
123 yStr = m.group(2)
125 print xNumber, ':', xStr
127 if xNumber and yNumber:
128 xNumber, yNumber = int(xNumber), int(yNumber)
129 if xNumber == yNumber:
130 return cmp(xStr, yStr)
131 else:
132 return cmp(xNumber, yNumber)
133 elif xNumber:
134 return -1
135 elif yNumber:
136 return 1
137 else:
138 return cmp(xStr, yStr)
140 files.sort(dir_sort)
142 index = 0
143 if query.has_key('ItemCount'):
144 count = int(query['ItemCount'] [0])
146 if query.has_key('AnchorItem'):
147 anchor = unquote(query['AnchorItem'][0])
148 for i in range(len(files)):
150 if os.path.isdir(os.path.join(path,files[i])):
151 file_url = '/TiVoConnect?Command=QueryContainer&Container=' + subcname + '/' + files[i]
152 else:
153 file_url = '/' + subcname + '/' + files[i]
154 if file_url == anchor:
155 index = i + 1
156 break
157 if query.has_key('AnchorOffset'):
158 index = index + int(query['AnchorOffset'][0])
159 files = files[index:index + count]
161 return files, totalFiles, index
163 def send_static(self, name, container):
165 #cheep hack
166 if self.headers.getheader('Range') and not self.headers.getheader('Range') == 'bytes=0-':
167 self.send_response(404)
168 self.end_headers()
169 return
171 o = urlparse("http://fake.host" + self.path)
172 path = unquote_plus(o.path)
173 self.send_response(200)
174 self.end_headers()
175 transcode.output_video(container['path'] + path[len(name)+1:], self.wfile)
177 def infopage(self):
178 self.send_response(200)
179 self.send_header('Content-type', 'text/html')
180 self.end_headers()
181 t = Template(file=os.path.join(SCRIPTDIR, 'templates', 'info_page.tmpl'))
182 self.wfile.write(t)
183 self.end_headers()
185 def unsuported(self, query):
186 self.send_response(404)
187 self.send_header('Content-type', 'text/html')
188 self.end_headers()
189 t = Template(file=os.path.join(SCRIPTDIR,'templates','unsuported.tmpl'))
190 t.query = query
191 self.wfile.write(t)
193 if __name__ == '__main__':
194 def start_server():
195 httpd = TivoHTTPServer(('', 9032), TivoHTTPHandler)
196 httpd.add_container('test', 'x-container/tivo-videos', r'C:\Documents and Settings\Armooo\Desktop\pyTivo\test')
197 httpd.serve_forever()
199 start_server()