mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-07-16 02:02:53 -07:00
Also updates: - colorama-0.4.1 - jellyfish-0.6.1 - munkres-1.0.12 - musicbrainzngs-0.6 - mutagen-1.41.1 - pyyaml-3.13 - six-1.12.0 - unidecode-1.0.23
314 lines
8.7 KiB
JavaScript
314 lines
8.7 KiB
JavaScript
// Format times as minutes and seconds.
|
|
var timeFormat = function(secs) {
|
|
if (secs == undefined || isNaN(secs)) {
|
|
return '0:00';
|
|
}
|
|
secs = Math.round(secs);
|
|
var mins = '' + Math.floor(secs / 60);
|
|
secs = '' + (secs % 60);
|
|
if (secs.length < 2) {
|
|
secs = '0' + secs;
|
|
}
|
|
return mins + ':' + secs;
|
|
}
|
|
|
|
// 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 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
|
|
$.fn.disableSelection = function() {
|
|
$(this).attr('unselectable', 'on')
|
|
.css('-moz-user-select', 'none')
|
|
.each(function() {
|
|
this.onselectstart = function() { return false; };
|
|
});
|
|
};
|
|
|
|
$(function() {
|
|
|
|
// Routes.
|
|
var BeetsRouter = Backbone.Router.extend({
|
|
routes: {
|
|
"item/query/:query": "itemQuery",
|
|
},
|
|
itemQuery: function(query) {
|
|
var queryURL = query.split(/\s+/).map(encodeURIComponent).join('/');
|
|
$.getJSON('item/query/' + queryURL, function(data) {
|
|
var models = _.map(
|
|
data['results'],
|
|
function(d) { return new Item(d); }
|
|
);
|
|
var results = new Items(models);
|
|
app.showItems(results);
|
|
});
|
|
}
|
|
});
|
|
var router = new BeetsRouter();
|
|
|
|
// Model.
|
|
var Item = Backbone.Model.extend({
|
|
urlRoot: 'item'
|
|
});
|
|
var Items = Backbone.Collection.extend({
|
|
model: Item
|
|
});
|
|
|
|
// Item views.
|
|
var ItemEntryView = Backbone.View.extend({
|
|
tagName: "li",
|
|
template: _.template($('#item-entry-template').html()),
|
|
events: {
|
|
'click': 'select',
|
|
'dblclick': 'play'
|
|
},
|
|
initialize: function() {
|
|
this.playing = false;
|
|
},
|
|
render: function() {
|
|
$(this.el).html(this.template(this.model.toJSON()));
|
|
this.setPlaying(this.playing);
|
|
return this;
|
|
},
|
|
select: function() {
|
|
app.selectItem(this);
|
|
},
|
|
play: function() {
|
|
app.playItem(this.model);
|
|
},
|
|
setPlaying: function(val) {
|
|
this.playing = val;
|
|
if (val)
|
|
this.$('.playing').show();
|
|
else
|
|
this.$('.playing').hide();
|
|
}
|
|
});
|
|
//Holds Title, Artist, Album etc.
|
|
var ItemMainDetailView = Backbone.View.extend({
|
|
tagName: "div",
|
|
template: _.template($('#item-main-detail-template').html()),
|
|
events: {
|
|
'click .play': 'play',
|
|
},
|
|
render: function() {
|
|
$(this.el).html(this.template(this.model.toJSON()));
|
|
return this;
|
|
},
|
|
play: function() {
|
|
app.playItem(this.model);
|
|
}
|
|
});
|
|
// Holds Track no., Format, MusicBrainz link, Lyrics, Comments etc.
|
|
var ItemExtraDetailView = Backbone.View.extend({
|
|
tagName: "div",
|
|
template: _.template($('#item-extra-detail-template').html()),
|
|
render: function() {
|
|
$(this.el).html(this.template(this.model.toJSON()));
|
|
return this;
|
|
}
|
|
});
|
|
// Main app view.
|
|
var AppView = Backbone.View.extend({
|
|
el: $('body'),
|
|
events: {
|
|
'submit #queryForm': 'querySubmit',
|
|
},
|
|
querySubmit: function(ev) {
|
|
ev.preventDefault();
|
|
router.navigate('item/query/' + encodeURIComponent($('#query').val()), true);
|
|
},
|
|
initialize: function() {
|
|
this.playingItem = null;
|
|
this.shownItems = null;
|
|
|
|
// Not sure why these events won't bind automatically.
|
|
this.$('audio').bind({
|
|
'play': _.bind(this.audioPlay, this),
|
|
'pause': _.bind(this.audioPause, this),
|
|
'ended': _.bind(this.audioEnded, this)
|
|
});
|
|
},
|
|
showItems: function(items) {
|
|
this.shownItems = items;
|
|
$('#results').empty();
|
|
items.each(function(item) {
|
|
var view = new ItemEntryView({model: item});
|
|
item.entryView = view;
|
|
$('#results').append(view.render().el);
|
|
});
|
|
},
|
|
selectItem: function(view) {
|
|
// Mark row as selected.
|
|
$('#results li').removeClass("selected");
|
|
$(view.el).addClass("selected");
|
|
|
|
// Show main and extra detail.
|
|
var mainDetailView = new ItemMainDetailView({model: view.model});
|
|
$('#main-detail').empty().append(mainDetailView.render().el);
|
|
|
|
var extraDetailView = new ItemExtraDetailView({model: view.model});
|
|
$('#extra-detail').empty().append(extraDetailView.render().el);
|
|
},
|
|
playItem: function(item) {
|
|
var url = 'item/' + item.get('id') + '/file';
|
|
$('#player audio').attr('src', url);
|
|
$('#player audio').get(0).play();
|
|
|
|
if (this.playingItem != null) {
|
|
this.playingItem.entryView.setPlaying(false);
|
|
}
|
|
item.entryView.setPlaying(true);
|
|
this.playingItem = item;
|
|
},
|
|
|
|
audioPause: function() {
|
|
this.playingItem.entryView.setPlaying(false);
|
|
},
|
|
audioPlay: function() {
|
|
if (this.playingItem != null)
|
|
this.playingItem.entryView.setPlaying(true);
|
|
},
|
|
audioEnded: function() {
|
|
this.playingItem.entryView.setPlaying(false);
|
|
|
|
// Try to play the next track.
|
|
var idx = this.shownItems.indexOf(this.playingItem);
|
|
if (idx == -1) {
|
|
// Not in current list.
|
|
return;
|
|
}
|
|
var nextIdx = idx + 1;
|
|
if (nextIdx >= this.shownItems.size()) {
|
|
// End of list.
|
|
return;
|
|
}
|
|
this.playItem(this.shownItems.at(nextIdx));
|
|
}
|
|
});
|
|
var app = new AppView();
|
|
|
|
// App setup.
|
|
Backbone.history.start({pushState: false});
|
|
|
|
// Disable selection on UI elements.
|
|
$('#entities ul').disableSelection();
|
|
$('#header').disableSelection();
|
|
|
|
// Audio player setup.
|
|
$('#player').player();
|
|
|
|
});
|