wplayer

Check-in [b0fbc45b9f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Permit player to be entered with "...?noflac", and have it receive a transcode to ogg.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:b0fbc45b9fad5d1ce8fbd498a69e5ff47b49df3caec0e3f8484dd39351e9acb3
User & Date: ajv-899-334-8894@vsta.org 2017-10-24 05:00:12
Context
2017-10-24
13:48
Fix regression for non-transcode code path check-in: b7a4eb540e user: ajv-899-334-8894@vsta.org tags: master, trunk
05:00
Permit player to be entered with "...?noflac", and have it receive a transcode to ogg. check-in: b0fbc45b9f user: ajv-899-334-8894@vsta.org tags: master, trunk
2017-06-24
04:33
Deal with non-ASCII file names check-in: 0544a82571 user: ajv-899-334-8894@vsta.org tags: master, trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to get.py.

4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
..
71
72
73
74
75
76
77



























































78
79
80
81
82
83
84
...
113
114
115
116
117
118
119

120



121
122
123
124









125
126
127
128
129
130
131
132
133
#
# Structure of paths in the server:
#  /
#	Main UI.  Top part is playlist, bottom is file/dir browser
#  /media/<prefix>/...
#	For each prefix of files, its contents is served by
#	way of its path under here.
import os, sys, stat, urllib
import utils
from chore.utils import uncharenc


# Max search results when filtering
MAXSEARCH=20

# Turn names into plain ASCII ones, with remap record so they
#  can still be opened
remaps = {}
................................................................................
    # Look up prefix, get state for this prefix
    def get_cpath(self, elem):
	cpaths = self.server.approot.config["files"]
	for tup in cpaths:
	    if tup[0] == elem:
		return tup[1]
        return None




























































    # GET dispatcher
    # See if it's a configured path, and serve file or dir
    def send_path(self):
        global remaps

	app = self.server
................................................................................
        indexed = ('_index' in d)
	if len(pp) > 1:
	    path = os.path.join(prefix, os.path.join(*pp[1:]))
	else:
	    path = prefix

	# File?  Or dir?

	st = os.stat(path)



	if stat.S_ISDIR(st.st_mode):
	    print "GET dir", path
	    buf = self._send_dir(indexed, path)
	else:









	    print "GET file", path
	    buf = self.send_files(path)
	return True,buf

    # A search request
    #
    # If there's an index for this part of the tree, search
    #  for up to MAXSEARCH results and provide links
    def search(self):







|


>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
|
>
>
>




>
>
>
>
>
>
>
>
>
|
|







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#
# Structure of paths in the server:
#  /
#	Main UI.  Top part is playlist, bottom is file/dir browser
#  /media/<prefix>/...
#	For each prefix of files, its contents is served by
#	way of its path under here.
import os, sys, stat, urllib, time
import utils
from chore.utils import uncharenc
from chore.handlers import sketchy

# Max search results when filtering
MAXSEARCH=20

# Turn names into plain ASCII ones, with remap record so they
#  can still be opened
remaps = {}
................................................................................
    # Look up prefix, get state for this prefix
    def get_cpath(self, elem):
	cpaths = self.server.approot.config["files"]
	for tup in cpaths:
	    if tup[0] == elem:
		return tup[1]
        return None

    # Run sox and stream ogg
    # This lets us transcode, specifically from flac, to things
    #  which don't play it (Firefox Android, nice job
    #  Mozilla) or where the network's too slow for it (DSL
    #  uplink).
    def send_ogg(self, path):

        # Sanity check usually done by send_files()
        # We also indulge in some paranoia on shell quoting
        if sketchy(path) or ('"' in path) or ('\\' in path):
            self.send_error(404)
            return None

        # What format are we coming from?
        mt = self.get_mtype(path) or (False, "BAD")
        if "flac" in mt[1]:
            mt = "flac"
        elif "wav" in mt[1]:
            mt = "wav"
        elif "ogg" in mt[1]:
            # Already ogg?
            return self.send_files(path)
        else:
            self.send_error(404)
            return None

        # Verify file exists
        try:
            f = open(path, "rb")
            st = os.fstat(f.fileno())
        except:
            self.send_error(404)
            return None
        f.close()

        # We serve the converted file by running a sub-process
        #  under a pipe, and pushing the bytes out to the user.
        # As a pipe, we don't know the overall length, nor
        #  can you seek over it.  Sorry.
        self.send_response(200)
        self.send_header("Content-type", "audio/ogg")
        self.send_header("Last-Mmodified", time.localtime(st.st_mtime))
        self.end_headers()

        # Here's the command chain, passed to a shell so we try
        #  to keep the filename from causing mischief.
        f = os.popen('sox -t %s - -t ogg - < "%s"' %
            (mt, path))
        try:
            while True:
                b = f.read(32768)
                if not b:
                    break
                self.wfile.write(b)
        finally:
            f.close()

        return None

    # GET dispatcher
    # See if it's a configured path, and serve file or dir
    def send_path(self):
        global remaps

	app = self.server
................................................................................
        indexed = ('_index' in d)
	if len(pp) > 1:
	    path = os.path.join(prefix, os.path.join(*pp[1:]))
	else:
	    path = prefix

	# File?  Or dir?
        try:
            st = os.stat(path)
        except:
            self.send_error(404)
            return True,None
	if stat.S_ISDIR(st.st_mode):
	    print "GET dir", path
	    buf = self._send_dir(indexed, path)
	else:
            # get /path/to/foo.flac?asogg -> stream a sox
            #  conversion of foo.flac
            # This lets us stream to players without flac
            #  support, or where the flac bitrate is too
            #  high for the network path.
            if "asogg" in self.vals:
                print "GET converted file", path
                buf = self.send_ogg(path)
            else:
                print "GET file", path
                buf = self.send_files(path)
	return True,buf

    # A search request
    #
    # If there's an index for this part of the tree, search
    #  for up to MAXSEARCH results and provide links
    def search(self):

Changes to js/player.js.

34
35
36
37
38
39
40


















41
42
43
44
45
46
47
...
181
182
183
184
185
186
187
188





189
190
191
192
193
194
195
var indexed = false;
// This will be path's contents; it goes to null while
//  it's getting new content for the current path
var pcontents = null;

// Do we have something on the turntable right now?
var playing = false;



















// Answer back on dir contents
function gotDir(req) {
    var d, f, res;

    // Connection progress
    if ((req.readyState != 4) || (req.status != 200)) {
................................................................................
function go_file(fidx) {
    // Working on a previous click
    if (pcontents == null) {
	return false;
    }

    // Assemble full filename
    var fname = "/media" + path + "/" + pcontents.files[fidx]





    if (!playing) {
	player.src = fname;
	nowplaying.textContent = do_trim(unescape(fname));
	player.play();
	playing = true;
    } else {
	playlist.push(fname);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>
>
>
>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
var indexed = false;
// This will be path's contents; it goes to null while
//  it's getting new content for the current path
var pcontents = null;

// Do we have something on the turntable right now?
var playing = false;

// One-time parse of base URL to find any player options
// Note we don't do any defense here, because the URL is under the
//  remote user's control, but it's only their own browser's JS which
//  will be "fooled".
var opts = {};
function urlOpts() {
  const query = location.search.substr(1);
  query.split("&").forEach(function(part) {
    const item = part.split("=");
    if (item.length == 1) {
        opts[item[0]] = true;
    } else {
        opts[item[0]] = item[1];
    }
  });
};
urlOpts();

// Answer back on dir contents
function gotDir(req) {
    var d, f, res;

    // Connection progress
    if ((req.readyState != 4) || (req.status != 200)) {
................................................................................
function go_file(fidx) {
    // Working on a previous click
    if (pcontents == null) {
	return false;
    }

    // Assemble full filename
    let fname = "/media" + path + "/" + pcontents.files[fidx]
    if (opts.noflac) {
        if (fname.endsWith(".flac")) {
            fname += "?asogg";
        }
    }
    if (!playing) {
	player.src = fname;
	nowplaying.textContent = do_trim(unescape(fname));
	player.play();
	playing = true;
    } else {
	playlist.push(fname);