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
9 SCRIPTDIR
= os
.path
.dirname(__file__
)
11 class TivoHTTPServer(SocketServer
.ThreadingMixIn
, BaseHTTPServer
.HTTPServer
):
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
):
26 for name
, container
in self
.server
.containers
.items():
28 if self
.path
.startswith('/' + name
):
29 self
.send_static(name
, container
)
32 if not self
.path
.startswith('/TiVoConnect'):
36 o
= urlparse("http://fake.host" + self
.path
)
37 query
= parse_qs(o
.query
)
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
)
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)
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)
69 path
= self
.get_local_path(query
)
71 return os
.path
.isdir(os
.path
.join(path
, file))
73 self
.send_response(200)
75 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates', 'container.tmpl'))
77 t
.files
, t
.total
, t
.start
= self
.get_files(query
)
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:]:
92 path
= os
.path
.join(path
, folder
)
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
)
104 xdir
= os
.path
.isdir(os
.path
.join(path
, x
))
105 ydir
= os
.path
.isdir(os
.path
.join(path
, y
))
108 return name_sort(x
, y
)
114 return name_sort(x
, y
)
117 numbername
= re
.compile(r
'(\d*)(.*)')
118 m
= numbername
.match(x
)
121 m
= numbername
.match(y
)
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
)
132 return cmp(xNumber
, yNumber
)
138 return cmp(xStr
, yStr
)
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
]
153 file_url
= '/' + subcname
+ '/' + files
[i
]
154 if file_url
== anchor
:
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
):
166 if self
.headers
.getheader('Range') and not self
.headers
.getheader('Range') == 'bytes=0-':
167 self
.send_response(404)
171 o
= urlparse("http://fake.host" + self
.path
)
172 path
= unquote_plus(o
.path
)
173 self
.send_response(200)
175 transcode
.output_video(container
['path'] + path
[len(name
)+1:], self
.wfile
)
178 self
.send_response(200)
179 self
.send_header('Content-type', 'text/html')
181 t
= Template(file=os
.path
.join(SCRIPTDIR
, 'templates', 'info_page.tmpl'))
185 def unsuported(self
, query
):
186 self
.send_response(404)
187 self
.send_header('Content-type', 'text/html')
189 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates','unsuported.tmpl'))
193 if __name__
== '__main__':
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()