diff --git a/beetsplug/web/static/audiojs/audio.js b/beetsplug/web/static/audiojs/audio.js deleted file mode 100644 index d675b9371..000000000 --- a/beetsplug/web/static/audiojs/audio.js +++ /dev/null @@ -1,24 +0,0 @@ -(function(g,r,f){var s=function(){for(var b=/audio(.min)?.js.*/,a=document.getElementsByTagName("script"),c=0,d=a.length;c ',settings:{autoplay:false,loop:false,preload:true,imageLocation:s+"player-graphics.gif",swfLocation:s+"audiojs.swf",useFlash:function(){var b=document.createElement("audio");return!(b.canPlayType&&b.canPlayType("audio/mpeg;").replace(/no/,""))}(),hasFlash:function(){if(navigator.plugins&&navigator.plugins.length&&navigator.plugins["Shockwave Flash"])return true;else if(navigator.mimeTypes&&navigator.mimeTypes.length){var b= -navigator.mimeTypes["application/x-shockwave-flash"];return b&&b.enabledPlugin}else try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash");return true}catch(a){}return false}(),createPlayer:{markup:'

00:00/00:00
', -playPauseClass:"play-pause",scrubberClass:"scrubber",progressClass:"progress",loaderClass:"loaded",timeClass:"time",durationClass:"duration",playedClass:"played",errorMessageClass:"error-message",playingClass:"playing",loadingClass:"loading",errorClass:"error"},css:' .audiojs audio { position: absolute; left: -1px; } .audiojs { width: 460px; height: 36px; background: #404040; overflow: hidden; font-family: monospace; font-size: 12px; background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(0.5, #555), color-stop(0.51, #444), color-stop(1, #444)); background-image: -moz-linear-gradient(center top, #444 0%, #555 50%, #444 51%, #444 100%); -webkit-box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.3); -moz-box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.3); -o-box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.3); box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.3); } .audiojs .play-pause { width: 25px; height: 40px; padding: 4px 6px; margin: 0px; float: left; overflow: hidden; border-right: 1px solid #000; } .audiojs p { display: none; width: 25px; height: 40px; margin: 0px; cursor: pointer; } .audiojs .play { display: block; } .audiojs .scrubber { position: relative; float: left; width: 280px; background: #5a5a5a; height: 14px; margin: 10px; border-top: 1px solid #3f3f3f; border-left: 0px; border-bottom: 0px; overflow: hidden; } .audiojs .progress { position: absolute; top: 0px; left: 0px; height: 14px; width: 0px; background: #ccc; z-index: 1; background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ccc), color-stop(0.5, #ddd), color-stop(0.51, #ccc), color-stop(1, #ccc)); background-image: -moz-linear-gradient(center top, #ccc 0%, #ddd 50%, #ccc 51%, #ccc 100%); } .audiojs .loaded { position: absolute; top: 0px; left: 0px; height: 14px; width: 0px; background: #000; background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #222), color-stop(0.5, #333), color-stop(0.51, #222), color-stop(1, #222)); background-image: -moz-linear-gradient(center top, #222 0%, #333 50%, #222 51%, #222 100%); } .audiojs .time { float: left; height: 36px; line-height: 36px; margin: 0px 0px 0px 6px; padding: 0px 6px 0px 12px; border-left: 1px solid #000; color: #ddd; text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.5); } .audiojs .time em { padding: 0px 2px 0px 0px; color: #f9f9f9; font-style: normal; } .audiojs .time strong { padding: 0px 0px 0px 2px; font-weight: normal; } .audiojs .error-message { float: left; display: none; margin: 0px 10px; height: 36px; width: 400px; overflow: hidden; line-height: 36px; white-space: nowrap; color: #fff; text-overflow: ellipsis; -o-text-overflow: ellipsis; -icab-text-overflow: ellipsis; -khtml-text-overflow: ellipsis; -moz-text-overflow: ellipsis; -webkit-text-overflow: ellipsis; } .audiojs .error-message a { color: #eee; text-decoration: none; padding-bottom: 1px; border-bottom: 1px solid #999; white-space: wrap; } .audiojs .play { background: url("$1") -2px -1px no-repeat; } .audiojs .loading { background: url("$1") -2px -31px no-repeat; } .audiojs .error { background: url("$1") -2px -61px no-repeat; } .audiojs .pause { background: url("$1") -2px -91px no-repeat; } .playing .play, .playing .loading, .playing .error { display: none; } .playing .pause { display: block; } .loading .play, .loading .pause, .loading .error { display: none; } .loading .loading { display: block; } .error .time, .error .play, .error .pause, .error .scrubber, .error .loading { display: none; } .error .error { display: block; } .error .play-pause p { cursor: auto; } .error .error-message { display: block; }', -trackEnded:function(){},flashError:function(){var b=this.settings.createPlayer,a=m(b.errorMessageClass,this.wrapper),c='Missing flash player plugin.';if(this.mp3)c+=' Download audio file.';f[g].helpers.removeClass(this.wrapper,b.loadingClass);f[g].helpers.addClass(this.wrapper,b.errorClass);a.innerHTML=c},loadError:function(){var b=this.settings.createPlayer,a=m(b.errorMessageClass,this.wrapper);f[g].helpers.removeClass(this.wrapper, -b.loadingClass);f[g].helpers.addClass(this.wrapper,b.errorClass);a.innerHTML='Error loading: "'+this.mp3+'"'},init:function(){f[g].helpers.addClass(this.wrapper,this.settings.createPlayer.loadingClass)},loadStarted:function(){var b=this.settings.createPlayer,a=m(b.durationClass,this.wrapper),c=Math.floor(this.duration/60),d=Math.floor(this.duration%60);f[g].helpers.removeClass(this.wrapper,b.loadingClass);a.innerHTML=(c<10?"0":"")+c+":"+(d<10?"0":"")+d},loadProgress:function(b){var a=this.settings.createPlayer, -c=m(a.scrubberClass,this.wrapper);m(a.loaderClass,this.wrapper).style.width=c.offsetWidth*b+"px"},playPause:function(){this.playing?this.settings.play():this.settings.pause()},play:function(){f[g].helpers.addClass(this.wrapper,this.settings.createPlayer.playingClass)},pause:function(){f[g].helpers.removeClass(this.wrapper,this.settings.createPlayer.playingClass)},updatePlayhead:function(b){var a=this.settings.createPlayer,c=m(a.scrubberClass,this.wrapper);m(a.progressClass,this.wrapper).style.width= -c.offsetWidth*b+"px";a=m(a.playedClass,this.wrapper);c=this.duration*b;b=Math.floor(c/60);c=Math.floor(c%60);a.innerHTML=(b<10?"0":"")+b+":"+(c<10?"0":"")+c}},create:function(b,a){a=a||{};return b.length?this.createAll(a,b):this.newInstance(b,a)},createAll:function(b,a){var c=a||document.getElementsByTagName("audio"),d=[];b=b||{};for(var e=0,k=c.length;ea.loadedPercent)){a.updatePlayhead.call(a,[c]);a.element.skipTo(c)}};a.updatePlayhead=function(c){a.settings.updatePlayhead.apply(a,[c])};a.play=function(){if(!a.settings.preload){a.settings.preload=true;a.element.init(a.mp3)}a.playing=true;a.element.pplay();a.settings.play.apply(a)};a.pause=function(){a.playing=false;a.element.ppause();a.settings.pause.apply(a)};a.loadStarted=function(){a.swfReady=true;a.settings.preload&&a.element.init(a.mp3); -a.settings.autoplay&&a.play.apply(a)}},injectFlash:function(b,a){var c=this.flashSource.replace(/\$1/g,a);c=c.replace(/\$2/g,b.settings.swfLocation);c=c.replace(/\$3/g,+new Date+Math.random());var d=b.wrapper.innerHTML,e=document.createElement("div");e.innerHTML=c+d;b.wrapper.innerHTML=e.innerHTML;b.element=this.helpers.getSwf(a)},helpers:{merge:function(b,a){for(attr in a)if(b.hasOwnProperty(attr)||a.hasOwnProperty(attr))b[attr]=a[attr]},clone:function(b){if(b==null||typeof b!=="object")return b; -var a=new b.constructor,c;for(c in b)a[c]=arguments.callee(b[c]);return a},addClass:function(b,a){RegExp("(\\s|^)"+a+"(\\s|$)").test(b.className)||(b.className+=" "+a)},removeClass:function(b,a){b.className=b.className.replace(RegExp("(\\s|^)"+a+"(\\s|$)")," ")},injectCss:function(b,a){for(var c="",d=document.getElementsByTagName("style"),e=a.replace(/\$1/g,b.settings.imageLocation),k=0,h=d.length;k1?b[b.length-1]:b}},events:{memoryLeaking:false,listeners:[],addListener:function(b,a,c){if(b.addEventListener)b.addEventListener(a,c,false);else if(b.attachEvent){this.listeners.push(b);if(!this.memoryLeaking){window.attachEvent("onunload",function(){for(var d=0,e=this.listeners.length;d-1)d||b.init.apply(b);if(b.element.readyState>1){b.settings.autoplay&&b.play.apply(b);clearInterval(a);c=setInterval(function(){b.loadProgress.apply(b);b.loadedPercent>=1&&clearInterval(c)})}},10);b.readyTimer=a;b.loadTimer=c}},purge:function(b){var a=b.attributes,c;if(a)for(c=0;cthis.loadedPercent)){this.element.currentTime=this.duration*b;this.updatePlayhead()}},load:function(b){this.loadStartedCalled=false;this.source.setAttribute("src",b);this.element.load();this.mp3=b;f[g].events.trackLoadProgress(this)},loadError:function(){this.settings.loadError.apply(this)},init:function(){this.settings.init.apply(this)}, -loadStarted:function(){if(!this.element.duration)return false;this.duration=this.element.duration;this.updatePlayhead();this.settings.loadStarted.apply(this)},loadProgress:function(){if(this.element.buffered!=null&&this.element.buffered.length){if(!this.loadStartedCalled)this.loadStartedCalled=this.loadStarted();this.loadedPercent=this.element.buffered.end(this.element.buffered.length-1)/this.duration;this.settings.loadProgress.apply(this,[this.loadedPercent])}},playPause:function(){this.playing? -this.pause():this.play()},play:function(){/(ipod|iphone|ipad)/i.test(navigator.userAgent)&&this.element.readyState==0&&this.init.apply(this);if(!this.settings.preload){this.settings.preload=true;this.element.setAttribute("preload","auto");f[g].events.trackLoadProgress(this)}this.playing=true;this.element.play();this.settings.play.apply(this)},pause:function(){this.playing=false;this.element.pause();this.settings.pause.apply(this)},trackEnded:function(){this.skipTo.apply(this,[0]);this.settings.loop|| -this.pause.apply(this);this.settings.trackEnded.apply(this)}};var m=function(b,a,c){var d=[];if(document.getElementsByClassName)d=a.getElementsByClassName(b);else{a=a||document;c=c||"*";a=a.getElementsByTagName(c);b=RegExp("(^|\\s)"+b+"(\\s|$)");j=i=0;for(l=a.length;i1?d:d[0]}})("audiojs","audiojsInstance",this); diff --git a/beetsplug/web/static/audiojs/audiojs.swf b/beetsplug/web/static/audiojs/audiojs.swf deleted file mode 100644 index 483599fc8..000000000 Binary files a/beetsplug/web/static/audiojs/audiojs.swf and /dev/null differ diff --git a/beetsplug/web/static/audiojs/player-graphics.gif b/beetsplug/web/static/audiojs/player-graphics.gif deleted file mode 100644 index 3e4d9d4e5..000000000 Binary files a/beetsplug/web/static/audiojs/player-graphics.gif and /dev/null differ diff --git a/beetsplug/web/static/beets.css b/beetsplug/web/static/beets.css index 4675064f3..62b667f66 100644 --- a/beetsplug/web/static/beets.css +++ b/beetsplug/web/static/beets.css @@ -116,25 +116,27 @@ body { margin-left: 10.5em; } -.audiojs { +#player { position: fixed; right: 0; top: 0; - width: 340px; + width: 150px; height: 36px; - +} +#player .play, #player .pause, #player .disabled { + -webkit-appearance: none; + font-size: 1em; + font-family: Helvetica, Arial, sans-serif; background: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - -o-box-shadow: none; - box-shadow: none; + border: none; + color: white; + padding: 5px; + margin: 0; + text-align: center; + + width: 36px; + height: 36px; } -.audiojs .play-pause { - border-left: 1px solid black; -} -.audiojs .time { - display: none; -} -.audiojs.unloaded .play-pause p { - display: none; +#player .disabled { + color: #666; } diff --git a/beetsplug/web/static/beets.js b/beetsplug/web/static/beets.js index d46452357..d2d59e769 100644 --- a/beetsplug/web/static/beets.js +++ b/beetsplug/web/static/beets.js @@ -1,3 +1,130 @@ +// jQuery extension encapsulating event hookups for audio element controls. +$.fn.player = function(debug) { + // Selected element should contain an HTML5 Audio element. + var audio = $('audio', this).get(0); + + // Control elements that may be present, identified by class. + var playBtn = $('.play', this); + var pauseBtn = $('.pause', this); + var disabledInd = $('.disabled', this); + var timesEl = $('.times', this); + var curTimeEl = $('.currentTime', this); + var totalTimeEl = $('.totalTime', this); + var sliderPlayedEl = $('.slider .played', this); + var sliderLoadedEl = $('.slider .loaded', this); + + // Button events. + playBtn.click(function() { + audio.play(); + }); + pauseBtn.click(function(ev) { + audio.pause(); + }); + + // Utilities. + var timeFormat = function(secs) { + if (secs == undefined || isNaN(secs)) { + return '0:00'; + } + secs = Math.round(secs); + var mins = '' + Math.round(secs / 60); + secs = '' + (secs % 60); + if (secs.length < 2) { + secs = '0' + secs; + } + return mins + ':' + secs; + } + var timePercent = function(cur, total) { + if (cur == undefined || isNaN(cur) || + total == undefined || isNaN(total) || total == 0) { + return 0; + } + var ratio = cur / total; + if (ratio > 1.0) { + ratio = 1.0; + } + return (Math.round(ratio * 10000) / 100) + '%'; + } + + // Event helpers. + var dbg = function(msg) { + if (debug) + console.log(msg); + } + var showState = function() { + if (audio.duration == undefined || isNaN(audio.duration)) { + playBtn.hide(); + pauseBtn.hide(); + disabledInd.show(); + timesEl.hide(); + } else if (audio.paused) { + playBtn.show(); + pauseBtn.hide(); + disabledInd.hide(); + timesEl.show(); + } else { + playBtn.hide(); + pauseBtn.show(); + disabledInd.hide(); + timesEl.show(); + } + } + var showTimes = function() { + curTimeEl.text(timeFormat(audio.currentTime)); + totalTimeEl.text(timeFormat(audio.duration)); + + sliderPlayedEl.css('width', + timePercent(audio.currentTime, audio.duration)); + + // last time buffered + var bufferEnd = 0; + for (var i = 0; i < audio.buffered.length; ++i) { + if (audio.buffered.end(i) > bufferEnd) + bufferEnd = audio.buffered.end(i); + } + sliderLoadedEl.css('width', + timePercent(bufferEnd, audio.duration)); + } + + // Initialize controls. + showState(); + showTimes(); + + // Bind events. + $('audio', this).bind({ + playing: function() { + dbg('playing'); + showState(); + }, + pause: function() { + dbg('pause'); + showState(); + }, + ended: function() { + dbg('ended'); + showState(); + }, + progress: function() { + dbg('progress ' + audio.buffered); + }, + timeupdate: function() { + dbg('timeupdate ' + audio.currentTime); + showTimes(); + }, + durationchange: function() { + dbg('durationchange ' + audio.duration); + showState(); + showTimes(); + }, + loadeddata: function() { + dbg('loadeddata'); + }, + loadedmetadata: function() { + dbg('loadedmetadata'); + } + }); +} + // Simple selection disable for jQuery. // Cut-and-paste from: // http://stackoverflow.com/questions/2700000 @@ -98,9 +225,8 @@ var AppView = Backbone.View.extend({ }, playItem: function(item) { var url = '/item/' + item.get('id') + '/file'; - $(audio.wrapper).removeClass('unloaded'); - audio.load(url); - audio.play(); + $('#player audio').attr('src', url); + $('#player audio').get(0).play(); } }); var app = new AppView(); @@ -113,11 +239,6 @@ $('#entities ul').disableSelection(); $('#header').disableSelection(); // Audio player setup. -var audio; -audiojs.events.ready(function() { - var as = audiojs.createAll(); - audio = as[0]; - $(audio.wrapper).addClass('unloaded'); -}); +$('#player').player(); }); diff --git a/beetsplug/web/templates/index.html b/beetsplug/web/templates/index.html index caa4f2900..4906f0ce3 100644 --- a/beetsplug/web/templates/index.html +++ b/beetsplug/web/templates/index.html @@ -10,8 +10,6 @@ - @@ -19,6 +17,15 @@

beets

+ + + + + + + + +