http://antimatter15.github.com/player/player.html
<!doctype html>
<html manifest="player.manifest">
<head>
<title>MP3 Player</title>
<link rel="icon" type="image/png" href="16.png">
<style>
body {
background: #444;
background-image: -webkit-gradient(
linear,
left top,
right top,
color-stop(0.8, rgb(133,133,133)),
color-stop(1, rgb(68,68,68))
);
background-image: -moz-linear-gradient(
left center,
rgb(133,133,133) 80%,
rgb(68,68,68) 100%
);
font-family: sans-serif;
font-size: 14px;
margin: 0;
overflow-y: scroll;
}
#startup #prompt {
width: 200px;
background: #ddd;
height: 20%;
width: 30%;
min-height: 180px;
border-radius: 5px;
z-index: 100;
position: absolute;
top: 35%;
left: 35%;
}
#mask {
position: absolute;
opacity: 0.5;
width: 100%;
left: 0;
top: 0;
height: 100%;
z-index: 4;
background: #000;
}
#prompt input {
margin-top: 15px;
margin-left: 15px;
}
#search {
width: 80%;
position:fixed;
top: 0;
padding:0;
margin:0;
}
#search input {
width: 100%;
height: 36px;
font-size: 30px;
border: none;
padding-left: 5px;
border-bottom: 1px solid #929292;
outline: none;
background: #929292;
background-image: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0, rgb(255,255,255)),
color-stop(1, rgb(221,221,221))
);
background-image: -moz-linear-gradient(
center top,
rgb(255,255,255) 0%,
rgb(221,221,221) 100%
);
}
audio {
width: 100%;
}
#footer {
position: fixed;
bottom: -3px;
width: 100%;
background: #858585;
}
#songs {
background: #ddd;
width: 80%;
padding-left: 5px;
}
#songs table {
width: 100%;
}
#playlist {
width: 20%;
position: fixed;
right: 0;
color: white;
top: 0;
}
#playlist table {
width: 100%;
margin: 5px;
margin-left: 10px;
}
table tr:hover {
background: #BBCEE9;
border-radius: 5px;
}
table tr {
-webkit-user-select: none;
user-select: none;
cursor: default;
}
table td {
padding-top: 3px;
padding-bottom: 3px;
padding-right: 5px;
}
#playlist table tr.playing {
/*background: #76AD8B;*/
font-style: italic;
}
#playlist table tr.playing td {
padding-left: 20px;
}
#listtools {
position: fixed;
bottom: 40px;
right: 5px;
color: #ddd;
}
#listtools a {
color: white;
text-decoration: none;
}
tr.hidden {
display:none;
}
</style>
<script src="id3v2.js"></script>
<script>
//beware! this code is pretty hacky and ugly.
/*
This doesn't use jQuery or any js libraries, not because they aren't great
in retrospect, I really think I should have used jQuery here.
But yesterday when I started this, I was really offended by a chrome extension
which was literally a one liner content script that had jquery as a dependency.
That's just terrible.
Anyway, this is more of a proof of concept and in a weird backwards way, I prefer
to do my prototypes without jQuery.
*/
function parseFile(file, callback){
if(localStorage[file.fileName]) return callback(JSON.parse(localStorage[file.fileName]));
ID3v2.parseFile(file,function(tags){
//to not overflow localstorage
localStorage[file.fileName] = JSON.stringify({
Title: tags.Title,
Artist: tags.Artist,
Album: tags.Album,
Genre: tags.Genre
});
callback(tags);
})
}
function runSearch(query){
console.log(query);
var regex = new RegExp(query.trim().replace(/\s+/g, '.*'), 'ig');
for(var i = $('songtable').getElementsByTagName('tr'), l = i.length; l--;){
if(regex.test(i[l].innerHTML)){
i[l].className = 'visible'
}else{
i[l].className = 'hidden';
}
}
}
function canPlay(type){
var a = document.createElement('audio');
return !!(a.canPlayType && a.canPlayType(type).replace(/no/, ''));
}
function $(id){return document.getElementById(id)}
function getSongs(files){
$("mask").style.display = 'none';
$("startup").style.display = 'none';
var queue = [];
var mp3 = canPlay('audio/mpeg;'), ogg = canPlay('audio/ogg; codecs="vorbis"');
for(var i = 0; i < files.length; i++){
var file = files[i];
var path = file.webkitRelativePath || file.mozFullPath || file.fileName;
if (path.indexOf('.AppleDouble') != -1) {
// Meta-data folder on Apple file systems, skip
continue;
}
var size = file.size || file.fileSize || 4096;
if(size < 4095) {
// Most probably not a real MP3
console.log(path);
continue;
}
if(file.fileName.indexOf('mp3') != -1){ //only does mp3 for now
if(mp3){
queue.push(file);
}
}
if(file.fileName.indexOf('ogg') != -1 || file.fileName.indexOf('oga') != -1){
if(ogg){
queue.push(file);
}
}
}
var process = function(){
if(queue.length){
var f = queue.shift();
parseFile(f,function(tags){
console.log(tags);
var tr = document.createElement('tr');
var t2 = guessSong(f.webkitRelativePath || f.mozFullPath || f.fileName);
//it should be innerText/contentText but its annoying.
var td = document.createElement('td');
td.innerHTML = tags.Title || t2.Title;
tr.appendChild(td);
var td = document.createElement('td');
td.innerHTML = tags.Artist || t2.Artist;
tr.appendChild(td);
var td = document.createElement('td');
td.innerHTML = tags.Album || t2.Album;
tr.appendChild(td);
var td = document.createElement('td');
td.innerHTML = tags.Genre || "";
tr.appendChild(td);
tr.onclick = function(){
var pl = document.createElement('tr');
var st = document.createElement('td');
st.innerHTML = tags.Title || t2.Title;
pl.appendChild(st);
$("playtable").appendChild(pl);
pl.file = f;
pl.className = 'visible';
pl.onclick = function(e){
if(e && e.button == 1){
pl.parentNode.removeChild(pl);
}else{
var url;
if(window.createObjectURL){
url = window.createObjectURL(f)
}else if(window.createBlobURL){
url = window.createBlobURL(f)
}else if(window.URL && window.URL.createObjectURL){
url = window.URL.createObjectURL(f)
}else if(window.webkitURL && window.webkitURL.createObjectURL){
url = window.webkitURL.createObjectURL(f)
}
$("player").src = url;
$("player").play();
for(var i = document.querySelectorAll('.playing'), l = i.length; l--;){
i[l].className = '';
}
pl.className += ' playing';
currentSong = pl;
}
}
if($("playtable").childNodes.length == 1) pl.onclick();
}
$('songtable').appendChild(tr);
process();
})
var lq = queue.length;
setTimeout(function(){
if(queue.length == lq){
process();
}
},300);
}
}
process();
console.log(files);
}
var currentSong = 0;
function nextSong(){
try{
currentSong.nextSibling.onclick();
}catch(e){
currentSong = document.querySelector("#playtable tr");
currentSong.onclick();
}
}
function shuffle(){
var pt = document.getElementById('playtable');
//fisher yates shuffle. hopefully.
for(var i = document.querySelectorAll("#playtable tr"), l = i.length; l--;){
var j = Math.floor(Math.random() * l);
var jel = i[j], iel = i[l];
var jref = jel.nextSibling, iref = iel.nextSibling;
pt.insertBefore(jel, iref);
pt.insertBefore(iel, jref);
}
}
function empty(){
var pt = document.getElementById('playtable');
pt.innerHTML = '';
}
onload = function(){
//with no dependencies, it should be fine to use this instead of ondomcontentloaded
var a = document.createElement('audio');
if(!a.canPlayType) $("support").innerHTML += "Your browser does not support HTML5 Audio<br>";
if(!(a.canPlayType && a.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, '')))
$("support").innerHTML += "Your browser does not support Ogg Vorbis Playback<br>";
if(!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, '')))
$("support").innerHTML += "Your browser does not support MP3 Playback<br>";
var f = document.createElement('input');
f.type = 'file';
if(!('multiple' in f)) $("support").innerHTML += "Your browser does not support selecting multiple files<br>";
if(!('webkitdirectory' in f)) $("support").innerHTML += "Your browser probably does not support selecting directories<br>";
if(window.createObjectURL){}else if(window.createBlobURL){}else if(window.URL && window.URL.createObjectURL){
}else if(window.webkitURL && window.webkitURL.createObjectURL){}else{
$("support").innerHTML += "Your browser probably does not support Object URLs<br>";
}
document.querySelector('#search input').onkeydown = function(e){
if(e.keyCode == 13){
for(var i = document.querySelectorAll('#songtable tr.visible'), l = i.length; l--;){
i[l].onclick();
}
}
}
}
</script>
</head>
<body>
<div id="mask"></div>
<div id="startup">
<div id="prompt">
<center>
<input type="file" webkitdirectory directory multiple mozdirectory onchange="getSongs(this.files)">
</center>
<p style="padding-left: 15px">
This is <b>almost certainly</b> the first mp3 player of its kind. Right above you should see a file prompt, go and select your <b>music folder</b>. It will index songs on your hard drive, read ID3 tags, play songs and it works offline. <b>HTML5 is awesome.</b>
</p>
<p style="padding-left: 15px">
By <a href="http://twitter.com/antimatter15">@antimatter15</a> <a href="http://antimatter15.com">http://antimatter15.com</a>
</p>
</div>
<div id="support" style="position:absolute;z-index:99999;color:red;font-size:x-large"> <!-- insert cheap knockoff modernizer clone -->
</div>
</div>
<div id="search">
<input type="text" placeholder="filter library" spellcheck=off autocomplete=off oninput="runSearch(this.value)">
</div>
<div style="height: 37px"></div>
<div id="playlist">
<table id="playtable"></table>
</div>
<div id="listtools">
<a href="javascript:shuffle()">Shuffle</a> /
<a href="javascript:empty()">Clear</a>
</div>
<div id="songs">
<table id="songtable" cellspacing=0 cellpadding=0>
</table>
</div>
<div style="height: 50px"></div>
<div id="footer">
<audio onended="nextSong()" controls id="player">
</div>
</body>
</html>