wplayer

Check-in [c475684d4b]
Login

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

Overview
Comment:Switch to Fossil. Add tinytag; update window title with artist/track.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:c475684d4bfd5f1296c9ca452aa591c4ed004b0c46ca95062bcab324d12ab822
User & Date: vandys 2018-06-12 22:51:32
Context
2018-06-12
22:52
Do my own styling. check-in: 78829a79ef user: vandys tags: master, trunk
22:51
Switch to Fossil. Add tinytag; update window title with artist/track. check-in: c475684d4b user: vandys tags: master, trunk
2018-04-28
00:37
Side-by-side sucked on mobile. Try picture below queue, and click to make it go away. check-in: c3a1651815 user: vandyswa@gmail.com tags: master, trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Deleted .gitignore.

     1         -chore
     2         -*.pyc
     3         -css
     4         -certs
     5         -var

Changes to get.py.

     8      8   #  /media/<prefix>/...
     9      9   #	For each prefix of files, its contents is served by
    10     10   #	way of its path under here.
    11     11   import os, sys, stat, urllib, time
    12     12   import utils
    13     13   from chore.utils import uncharenc
    14     14   from chore.handlers import sketchy
           15  +from tinytag import TinyTag
    15     16   
    16     17   # Max search results when filtering
    17     18   MAXSEARCH=20
    18     19   
    19     20   # Turn names into plain ASCII ones, with remap record so they
    20     21   #  can still be opened
    21     22   remaps = {}
................................................................................
    75     76   	for tup in cpaths:
    76     77   	    if tup[0] == elem:
    77     78   		return tup[1]
    78     79           return None
    79     80   
    80     81       # Run sox and stream ogg
    81     82       # This lets us transcode, specifically from flac, to things
    82         -    #  which don't play it (Firefox Android, nice job
    83         -    #  Mozilla) or where the network's too slow for it (DSL
           83  +    #  which don't play it (old Firefox Android, but they fixed
           84  +    #  it, yay!) or where the network's too slow for it (DSL
    84     85       #  uplink).
    85     86       def send_ogg(self, path):
    86     87   
    87     88           # Sanity check usually done by send_files()
    88     89           # We also indulge in some paranoia on shell quoting
    89     90           if sketchy(path) or ('"' in path) or ('\\' in path):
    90     91               self.send_error(404)
................................................................................
   132    133                       break
   133    134                   self.wfile.write(b)
   134    135           finally:
   135    136               f.close()
   136    137   
   137    138           return None
   138    139   
          140  +    # Get tinytag's take on metadata in this file, and return it
          141  +    def metadata(self, fn):
          142  +	# Pull tags if we can
          143  +	try:
          144  +	    tags = TinyTag.get(fn)
          145  +	except:
          146  +	    return self.send_error(422)
          147  +
          148  +	# Convert from attributes on object to JSON-able dict
          149  +	res = {}
          150  +	for k,v in tags.__dict__.iteritems():
          151  +
          152  +	    # Internal stuff from TinyTag
          153  +	    if k[0] == '_':
          154  +		continue
          155  +
          156  +	    # Straight ASCII here
          157  +	    if isinstance(v, unicode):
          158  +		v = str(v)
          159  +
          160  +	    # Inherently numeric channels
          161  +	    if k in ("year", "channels", "track", "disc", "track_total",
          162  +		    "disc_total"):
          163  +		if v and isinstance(v, str) and v.isdigit():
          164  +		    v = int(v)
          165  +
          166  +	    # Build our dict result
          167  +	    res[k] = v
          168  +
          169  +	# JSON encoded result
          170  +	return self.send_json(res)
          171  +
   139    172       # GET dispatcher
   140    173       # See if it's a configured path, and serve file or dir
   141    174       def send_path(self):
   142    175           global remaps
   143    176   
   144    177   	app = self.server
   145    178   	webroot = app.approot
   146    179   	cpaths = webroot.config["files"]
          180  +
   147    181           # Get URL level quoting out of the way
   148    182   	pp = [urllib.unquote(p) for p in self.paths]
          183  +
   149    184           # If we had to flatten out characters, restore
   150    185           #  to filesystem name
   151    186           pp = [remaps.get(p, p) for p in pp]
   152    187   
   153    188   	# We serve /media/prefix/blah/blah...
   154    189   	if pp[0] != "media":
   155    190   	    return False,None
................................................................................
   190    225               #  conversion of foo.flac
   191    226               # This lets us stream to players without flac
   192    227               #  support, or where the flac bitrate is too
   193    228               #  high for the network path.
   194    229               if self.vals and ("asogg" in self.vals):
   195    230                   print "GET converted file", path
   196    231                   buf = self.send_ogg(path)
          232  +
          233  +	    # get /path/to/foo.mp3?meta
          234  +	    # Send back JSON of the metadata of the file
          235  +	    elif self.vals and ("metadata" in self.vals):
          236  +		print "Metadata for", path
          237  +		buf = self.metadata(path)
          238  +
   197    239               else:
   198    240                   print "GET file", path
   199    241                   buf = self.send_files(path)
   200    242   	return True,buf
   201    243   
   202    244       # A search request
   203    245       #

Changes to html/top.html.

     2      2   <link rel="stylesheet" type="text/css" href="/css/player.css" />
     3      3   <head>
     4      4    <meta charset="utf-8">
     5      5    <meta name="viewport" content="width=device-width, initial-scale=1">
     6      6    <title>Web Player</title>
     7      7   </head>
     8      8   <body>
     9         - <textarea rows="8" cols="80" readonly id="pqueue"></textarea><br>
            9  + <textarea rows="8" readonly id="pqueue"></textarea><br>
    10     10    <img onclick="unplayart();" style="display: none;" id="playart">
    11     11    <hr>
    12     12    <span id="nowplaying"></span><br>
    13     13    <table>
    14     14     <tr>
    15     15      <td>
    16     16       <audio controls autoplay id="player" style="text-align: center;"></audio>

Changes to js/player.js.

   214    214       }
   215    215   }
   216    216   
   217    217   // Does this path look art-ish?
   218    218   function isart(fn) {
   219    219       return (fn.endsWith(".jpg") || fn.endsWith(".png"));
   220    220   }
          221  +
          222  +// Ask the server to extract the metadata for this track
          223  +// If we get it, update the window title so things like lock screen
          224  +//  hold something helpful.
          225  +function paint_metadata(s) {
          226  +    let req = new XMLHttpRequest();
          227  +    req.open("GET", s + "?metadata");
          228  +    req.onreadystatechange = function() {
          229  +	if (req.readyState == 4) {
          230  +	    if (req.status == 200) {
          231  +		let meta = JSON.parse(this.responseText);
          232  +		window.document.title =
          233  +		    (meta.artist || "") + " - " +
          234  +		    (meta.title || "");
          235  +	    } else {
          236  +		// No metadata available this time
          237  +		window.document.title = "WWW Player";
          238  +	    }
          239  +	}
          240  +    }
          241  +    req.send();
          242  +}
   221    243   
   222    244   // End of current track, try to advance
   223    245   function playNext() {
   224    246       lovebtn.style.background = "";
   225    247       while (playlist.length > 0) {
   226    248   	var s = playlist.shift();
   227    249   
................................................................................
   234    256   	}
   235    257   
   236    258   	// Play it
   237    259   	player.src = s;
   238    260   
   239    261   	// For good measure
   240    262   	player.play();
          263  +	playing = true;
   241    264   
          265  +	// Update UI
   242    266   	nowplaying.textContent = do_trim(unescape(s));
   243    267   	paintQueue();
   244         -	playing = true;
          268  +	paint_metadata(s);
   245    269   
   246    270   	return;
   247    271       }
   248    272   
   249    273       // Nothing to play
   250    274       nowplaying.textContent = '';
   251    275       playing = false;
................................................................................
   281    305   
   282    306       // Play or queue a track
   283    307       if (!playing) {
   284    308   	player.src = fname;
   285    309   	nowplaying.textContent = do_trim(unescape(fname));
   286    310   	player.play();
   287    311   	playing = true;
          312  +	paint_metadata(fname);
   288    313       } else {
   289    314   	playlist.push(fname);
   290    315   	paintQueue();
   291    316       }
   292    317       return false;
   293    318   }
   294    319