Switch to unified view

a/src/mediaserver/cdplugins/uprcl/uprclhttp.py b/src/mediaserver/cdplugins/uprcl/uprclhttp.py
...
...
21
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
from __future__ import print_function
22
from __future__ import print_function
23
23
24
import SocketServer
24
import SocketServer
25
import BaseHTTPServer
25
import BaseHTTPServer
26
import SimpleHTTPServer
27
import os
26
import os
28
import posixpath
27
import posixpath
29
import BaseHTTPServer
30
import urllib
28
import urllib
31
import cgi
29
import urlparse
32
import shutil
30
import shutil
33
import mimetypes
31
import mimetypes
34
import sys
32
import sys
33
34
import mutagen
35
35
36
try:
36
try:
37
    from cStringIO import StringIO
37
    from cStringIO import StringIO
38
except ImportError:
38
except ImportError:
39
    from StringIO import StringIO
39
    from StringIO import StringIO
40
40
41
from uprclutils import uplog
41
from uprclutils import uplog,printable
42
43
42
44
43
45
__version__ = "0.1"
44
__version__ = "0.1"
46
45
47
class RangeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
46
class RangeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
...
...
93
        to the outputfile by the caller unless the command was HEAD,
92
        to the outputfile by the caller unless the command was HEAD,
94
        and must be closed by the caller under all circumstances), or
93
        and must be closed by the caller under all circumstances), or
95
        None, in which case the caller has nothing further to do.
94
        None, in which case the caller has nothing further to do.
96
95
97
        """
96
        """
98
        uplog("HTTP: path: %s" % self.path)
97
99
        path = self.translate_path(self.path)
98
        path,embedded = self.translate_path(self.path)
100
        uplog("HTTP: translated path: %s" % urllib.quote(path))
99
        #uplog("HTTP: translated: embedded %s path: %s" %
100
        #      (embedded, printable(path)))
101
101
        if not path or not os.path.exists(path):
102
        if not path or not os.path.exists(path):
102
            self.send_error(404)
103
            self.send_error(404)
103
            return (None, 0, 0)
104
            return (None, 0, 0)
104
105
105
        if not os.path.isfile(path):
106
        if not os.path.isfile(path):
106
            self.send_error(405)
107
            self.send_error(405)
107
            return (None, 0, 0)
108
            return (None, 0, 0)
108
109
109
        f = None
110
        f = None
110
        ctype = self.guess_type(path)
111
        try:
111
        try:
112
            if embedded:
113
                ctype, size, f = self.embedded_open(path)
114
                fs = os.stat(path)
115
                #uplog("embedded, got ctype %s size %s" %(ctype, size))
116
            else:
117
                ctype = self.guess_type(path)
112
            f = open(path, 'rb')
118
                f = open(path, 'rb')
113
        except:
119
                fs = os.fstat(f.fileno())
120
                size = int(fs[6])
121
        except Exception as err:
114
            self.send_error(404, "File not found")
122
            self.send_error(404, "File not found")
115
            return (None, 0, 0)
123
            return (None, 0, 0)
116
124
117
        if "Range" in self.headers:
125
        if "Range" in self.headers:
118
            self.send_response(206)
126
            self.send_response(206)
119
        else:
127
        else:
120
            self.send_response(200)
128
            self.send_response(200)
121
129
130
        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
122
        self.send_header("Content-type", ctype)
131
        self.send_header("Content-type", ctype)
123
        fs = os.fstat(f.fileno())
124
        size = int(fs[6])
125
        start_range = 0
132
        start_range = 0
126
        end_range = size
133
        end_range = size
127
        self.send_header("Accept-Ranges", "bytes")
134
        self.send_header("Accept-Ranges", "bytes")
128
        if "Range" in self.headers:
135
        if "Range" in self.headers:
129
            s, e = self.headers['range'][6:].split('-', 1)
136
            s, e = self.headers['range'][6:].split('-', 1)
...
...
139
                    start_range = size - ei
146
                    start_range = size - ei
140
        self.send_header("Content-Range",
147
        self.send_header("Content-Range",
141
                         'bytes ' + str(start_range) + '-' +
148
                         'bytes ' + str(start_range) + '-' +
142
                         str(end_range - 1) + '/' + str(size))
149
                         str(end_range - 1) + '/' + str(size))
143
        self.send_header("Content-Length", end_range - start_range)
150
        self.send_header("Content-Length", end_range - start_range)
144
        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
145
        self.end_headers()
151
        self.end_headers()
146
        #uplog("Sending Bytes %d to %d" % (start_range, end_range))
152
        #uplog("Sending Bytes %d to %d" % (start_range, end_range))
147
        return (f, start_range, end_range)
153
        return (f, start_range, end_range)
148
154
149
155
150
    def translate_path(self, opath):
156
    def translate_path(self, opath):
151
        path = urllib.unquote(opath)
157
        path = urllib.unquote(opath)
152
        path = path.replace(self.uprclpathprefix, '', 1)
158
        path = path.replace(self.uprclpathprefix, '', 1)
159
160
        q = urlparse.urlparse(path)
161
        path = q.path
162
        embedded = False
163
        pq = urlparse.parse_qs(q.query)
164
        if 'embed' in pq:
165
            embedded = True
166
153
        for fsp,htp in self.uprclpathmap.iteritems():
167
        for fsp,htp in self.uprclpathmap.iteritems():
154
            if path.startswith(fsp):
168
            if path.startswith(fsp):
155
                return path.replace(fsp, htp, 1)
169
                path = path.replace(fsp, htp, 1)
170
                if embedded:
171
                    # Embedded image urls have had a .jpg or .png
172
                    # appended. Remove it to restore the track path
173
                    # name.
174
                    i = path.rfind('.')
175
                    path = path[:i]
176
                return path, embedded
156
177
178
        # Security feature here: never allow access to anything not in
179
        # the path map
157
        uplog("HTTP: translate_path: %s not found in path map" % opath)
180
        uplog("HTTP: translate_path: %s not found in path map" % opath)
158
        return None
181
        return None, None
182
183
184
    # Open embedded image. Returns mtype, size, f
185
    def embedded_open(self, path):
186
        try:
187
            mutf = mutagen.File(path)
188
        except Exception as err:
189
            raise err
190
        
191
        f = None
192
        size = 0
193
        if 'audio/mp3' in mutf.mime:
194
            for tagname in mutf.iterkeys():
195
                if tagname.startswith('APIC:'):
196
                    #self.em.rclog("mp3 img: %s" % mutf[tagname].mime)
197
                    mtype = mutf[tagname].mime
198
                    s = mutf[tagname].data
199
                    size = len(s)
200
                    f = StringIO(s)
201
        elif 'audio/x-flac' in mutf.mime:
202
            if mutf.pictures:
203
                mtype = mutf.pictures[0].mime
204
                size = len(mutf.pictures[0].data)
205
                f = StringIO(mutf.pictures[0].data)
206
        elif 'audio/mp4' in mutf.mime:
207
            if 'covr' in mutf.iterkeys():
208
                format = mutf['covr'][0].imageformat 
209
                if format == mutagen.mp4.AtomDataType.JPEG:
210
                    mtype = 'image/jpeg'
211
                else:
212
                    mtype = 'image/png'
213
                size = len(mutf['covr'][0])
214
                f = StringIO(mutf['covr'][0])
215
        if f is None:
216
            raise Exception("can't open embedded image")
217
        else:
218
            return mtype, size, f
159
219
160
    def guess_type(self, path):
220
    def guess_type(self, path):
161
        """Guess the type of a file.
221
        """Guess the type of a file.
162
222
163
        Argument is a PATH (a filename).
223
        Argument is a PATH (a filename).