--- a
+++ b/src/mediaserver/cdplugins/uprcl/uprclhttp.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 J.F.Dockes
+#
+# HTTP Range code:
+#  Portions Copyright (C) 2009,2010  Xyne
+#  Portions Copyright (C) 2011 Sean Goller
+#  https://github.com/smgoller/rangehttpserver/blob/master/RangeHTTPServer.py
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import SocketServer
+import BaseHTTPServer
+import SimpleHTTPServer
+import os
+import posixpath
+import BaseHTTPServer
+import urllib
+import cgi
+import shutil
+import mimetypes
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+from uprclutils import uplog
+
+
+
+__version__ = "0.1"
+
+class RangeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    """Simple HTTP request handler with GET and HEAD commands.
+
+    This serves files from the current directory and any of its
+    subdirectories.  The MIME type for files is determined by
+    calling the .guess_type() method.
+
+    The GET and HEAD requests are identical except that the HEAD
+    request omits the actual contents of the file.
+
+    """
+
+    server_version = "RangeHTTP/" + __version__
+
+    def do_GET(self):
+        """Serve a GET request."""
+        f, start_range, end_range = self.send_head()
+        if f:
+            uplog("do_GET: Got (%d,%d)" % (start_range,end_range))
+            f.seek(start_range, 0)
+            chunk = 0x1000
+            total = 0
+            while chunk > 0:
+                if start_range + chunk > end_range:
+                    chunk = end_range - start_range
+                try:
+                    self.wfile.write(f.read(chunk))
+                except:
+                    break
+                total += chunk
+                start_range += chunk
+            f.close()
+
+    def do_HEAD(self):
+        """Serve a HEAD request."""
+        f, start_range, end_range = self.send_head()
+        if f:
+            f.close()
+
+    def send_head(self):
+        """Common code for GET and HEAD commands.
+
+        This sends the response code and MIME headers.
+
+        Return value is either a file object (which has to be copied
+        to the outputfile by the caller unless the command was HEAD,
+        and must be closed by the caller under all circumstances), or
+        None, in which case the caller has nothing further to do.
+
+        """
+        uplog("HTTP: path: %s" % self.path)
+        path = self.translate_path(self.path)
+        if not path or not os.path.exists(path):
+            self.send_error(404)
+            return (None, 0, 0)
+
+        if not os.path.isfile(path):
+            self.send_error(405)
+            return (None, 0, 0)
+
+        f = None
+        ctype = self.guess_type(path)
+        try:
+            f = open(path, 'rb')
+        except:
+            self.send_error(404, "File not found")
+            return (None, 0, 0)
+
+        if "Range" in self.headers:
+            self.send_response(206)
+        else:
+            self.send_response(200)
+
+        self.send_header("Content-type", ctype)
+        fs = os.fstat(f.fileno())
+        size = int(fs[6])
+        start_range = 0
+        end_range = size
+        self.send_header("Accept-Ranges", "bytes")
+        if "Range" in self.headers:
+            s, e = self.headers['range'][6:].split('-', 1)
+            sl = len(s)
+            el = len(e)
+            if sl > 0:
+                start_range = int(s)
+                if el > 0:
+                    end_range = int(e) + 1
+            elif el > 0:
+                ei = int(e)
+                if ei < size:
+                    start_range = size - ei
+        self.send_header("Content-Range",
+                         'bytes ' + str(start_range) + '-' +
+                         str(end_range - 1) + '/' + str(size))
+        self.send_header("Content-Length", end_range - start_range)
+        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
+        self.end_headers()
+        #uplog("Sending Bytes %d to %d" % (start_range, end_range))
+        return (f, start_range, end_range)
+
+
+    def translate_path(self, opath):
+        path = urllib.unquote(opath)
+        for p in self.uprclpathmap.itervalues():
+            if path.startswith(p):
+                return path
+        uplog("HTTP: translate_path: %s not found in path map" % opath)
+        return None
+
+    def guess_type(self, path):
+        """Guess the type of a file.
+
+        Argument is a PATH (a filename).
+
+        Return value is a string of the form type/subtype,
+        usable for a MIME Content-type header.
+
+        The default implementation looks the file's extension
+        up in the table self.extensions_map, using application/octet-stream
+        as a default; however it would be permissible (if
+        slow) to look inside the data to make a better guess.
+
+        """
+
+        base, ext = posixpath.splitext(path)
+        if ext in self.extensions_map:
+            return self.extensions_map[ext]
+        ext = ext.lower()
+        if ext in self.extensions_map:
+            return self.extensions_map[ext]
+        else:
+            return self.extensions_map['']
+
+    if not mimetypes.inited:
+        mimetypes.init() # try to read system mime.types
+    extensions_map = mimetypes.types_map.copy()
+    extensions_map.update({
+        '': 'application/octet-stream', # Default
+        '.mp4': 'video/mp4',
+        '.ogg': 'video/ogg',
+        })
+
+
+class ThreadingSimpleServer(SocketServer.ThreadingMixIn,
+                            BaseHTTPServer.HTTPServer):
+    pass
+
+
+def runHttp(host='', port=8080, pathmap={}):
+
+    # Set pathmap as request handler class variable
+    RangeHTTPRequestHandler.uprclpathmap = pathmap
+    
+    server = ThreadingSimpleServer((host, port), RangeHTTPRequestHandler)
+    while 1:
+        server.handle_request()