audio = null; playlist = null; // pre-load some icons cache_images = new Array( 'music-cached.png', 'music-queued.png', 'loading.gif' ); var img = new Image(); for(var i = 0; i < cache_images.length; i++) { img.src = '/static/icons/' + cache_images[i]; } function MusicListing(type, path, name, track, metadata, cached) { this.type = type; this.path = path; this.name = name ? name : path.split('/').pop(); this.track = track; this.metadata = metadata; this.a = document.createElement('a'); this.a.ml = this; this.get_text = function() { if(!metadata) return name; var s = ''; if('artist' in metadata) s = metadata['artist'] + ' - '; if('title' in metadata) s += metadata['title']; if(s.length > 0 && track) s = track + ' ' + s; if(s.length == 0) s = name; return s; } this.play = function() { var path = '/play?path=' + encodeURIComponent(this.path); if(this.track) path += '&track=' + this.track; log('playing ' + path); xmlhttp = new XMLHttpRequest(); xmlhttp.open('GET', path); xmlhttp.send(null); } this.recode = function() { var path = '/recode?path=' + encodeURIComponent(this.path); if(this.track) path += '&track=' + this.track; var a = this.a; var ml = this; var xmlhttp = new XMLHttpRequest(); a.setAttribute('class', 'file file-queued'); xmlhttp.open('GET', path); xmlhttp.send(null); } this.get_anchor = function() { var a = this.a; var className = type; if(cached) className += ' file-cached' a.setAttribute('class', className); a.setAttribute('href', '#'); var name = this.get_text(); a.appendChild(document.createTextNode(name)); var ml = this; a.onclick = function() { if(type == 'dir') { browser.list(path); } else if(type == 'file') { playlist.add(ml); } return false; } return a; } } function log(s) { logbox = document.getElementById('logbox'); logbox.value += s + '\n'; logbox.scrollTop = logbox.scrollHeight; } function Browser(songs, current, audio) { this.songs = songs; this.current = current; this.audio = audio; this.set_current = function(path) { this.current.innerHTML = 'Directory: ' + path; } this.output_link = function(obj) { var a = obj.get_anchor(); var li = document.createElement('li'); li.appendChild(a); this.songs.appendChild(li); } this.output_album = function(album) { var li = document.createElement('li'); li.setAttribute('class', 'album'); li.appendChild(document.createTextNode(album ? album : '(No album)')); var song_links = document.getElementById('song-links'); song_links.appendChild(li); } this.add_results = function(json) { var lastalbum = false; for(var i = 0; i < json.length; i++) { var type = json[i]["type"]; var path = json[i]["name"]; var track = json[i]["track"]; var name = path.substring(path.lastIndexOf('/')+1); var cached = type == "file" ? json[i]["cached"] : false; var metadata = json[i]["metadata"]; if(type == 'file' && metadata.album != lastalbum) { this.output_album(metadata.album); lastalbum = metadata.album; } var l = new MusicListing(type, path, name, track, metadata, cached); this.output_link(l); } log('add_results: ' + lastalbum); } this.list = function(root) { var b = this; log('listing ' + root); var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if(xmlhttp.readyState == 4) { b.set_current(root); var json = JSON.parse(xmlhttp.responseText); document.getElementById('song-links').innerHTML = ''; // add "up" link if(root.length > 1) { up = root.substr(0, root.lastIndexOf('/')); if(up.length == 0) up = '/'; l = new MusicListing('dir', up, '..'); b.output_link(l); } b.add_results(json); } } path = '/list?directory=' + encodeURIComponent(root); xmlhttp.open('GET', path); xmlhttp.send(); } this.do_search = function(e) { var b = this; if(e.keyCode != 13) return; var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if(xmlhttp.readyState == 4) { var json = JSON.parse(xmlhttp.responseText); document.getElementById('song-links').innerHTML = ''; b.output_link(new MusicListing('dir', '/', 'Go to root directory')); b.add_results(json); } } var query = document.getElementById('search-query').value; var path = '/search?query=' + encodeURIComponent(query); xmlhttp.open('GET', path); xmlhttp.send(); } this.add_directory = function() { var songs = document.getElementById('song-links').getElementsByTagName('a'); for(var i = 0; i < songs.length; i++) { var a = songs[i]; log(a); var ml = a.ml; log(ml); if(ml.type && ml.type == 'file') playlist.add(ml); } } this.get_a = function(path, track) { var as = document.getElementById('song-links').getElementsByTagName('a'); for(var i = 0; i < as.length; i++) { var a = as[i]; if(a.ml.path == path && (!track || track == a.ml.track)) return a; } } } var source = null; function event_handler(event) { data = JSON.parse(event.data); switch(data['type']) { case 'cached': case 'recoding': log('[' + data['type'] + '] ' + data['path']); var track = null; if('track' in data) track = data['track']; log('track: ' + track); // update directory browser var a = browser.get_a(data['path'], track); if(a) a.setAttribute('class', 'file file-' + data['type']); // update song queue var li = playlist.get(data['path'], track); if(li) { a = li.getElementsByTagName('a')[0]; if(data['type'] == 'cached') { // play the current song if we were waiting for it if(li == playlist.current) a.onclick(); else a.setAttribute('class', 'song'); } else { a.setAttribute('class', 'song file-recoding'); } } break; case 'play': log('[play] ' + data['path']); browser.audio.set_src(data['path']); audio.play(); break; default: log('[event] unknown type: ' + data['type']); } } function event_open() { if(source) source.close(); source = new EventSource('/events'); source.onopen = function() { log('event source opened'); } source.onmessage = event_handler; source.onerror = function(event) { log('event source error'); } log('event source status: ' + source.readyState); } window.onload = function() { event_open(); var song_links = document.getElementById('song-links'); var current = document.getElementById('current-dir'); audio = new Audio(); browser = new Browser(song_links, current, audio); playlist = new Playlist(document.getElementById('playlist')); audio.audio.addEventListener('ended', function() { playlist.next(); }); browser.list('/'); }