450 lines
16 KiB
JavaScript
450 lines
16 KiB
JavaScript
var snapcast = function() {
|
|
|
|
var groups = new Array();
|
|
var streams = new Array();
|
|
var updatetimer = null;
|
|
var id = 0;
|
|
var lastid = 0;
|
|
var ew = null;
|
|
|
|
function snapcastRequest(parms, callback) {
|
|
clearTimeout(updatetimer);
|
|
id++;
|
|
parms.id = id;
|
|
parms.jsonrpc = "2.0";
|
|
$.ajax({
|
|
type: 'POST',
|
|
url: 'snapcast/snapapi.php',
|
|
data: JSON.stringify(parms)
|
|
})
|
|
.done(callback)
|
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
|
debug.error("SNAPCAST","Command Failed",parms,textStatus,errorThrown);
|
|
callback({error: language.gettext('snapcast_notthere')});
|
|
})
|
|
}
|
|
|
|
function findGroup(arr, id) {
|
|
for (var i in arr) {
|
|
if (arr[i].getId() == id) {
|
|
return i;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return {
|
|
updateStatus: function() {
|
|
clearTimeout(updatetimer);
|
|
if (prefs.snapcast_server != '' && prefs.snapcast_port != '') {
|
|
snapcastRequest({
|
|
method: "Server.GetStatus"
|
|
}, snapcast.gotStatus);
|
|
$('#snapheader').removeClass('invisible');
|
|
}
|
|
},
|
|
|
|
gotStatus: function(data) {
|
|
debug.trace("SNAPCAST", "Server Status", data);
|
|
if (data.hasOwnProperty('error') && lastid != id) {
|
|
if (ew === null) {
|
|
ew = $('<div>', {class: "fullwidth textcentre", style: "padding:4px"}).insertBefore('#snapcastgroups');
|
|
$('#snapcastgroups').addClass('canbefaded').css({opacity: '0.4'});
|
|
}
|
|
ew.html('<b>'+data.error+'</b>');
|
|
} else if (data.hasOwnProperty('id') && parseInt(data.id) == id) {
|
|
lastid = id;
|
|
if (ew !== null) {
|
|
ew.remove();
|
|
ew = null;
|
|
$('#snapcastgroups').removeClass('canbefaded').css({opacity: ''});
|
|
}
|
|
streams = data.result.server.streams;
|
|
var newgroups = new Array();
|
|
for (var i in data.result.server.groups) {
|
|
var group_exists = findGroup(groups, data.result.server.groups[i].id);
|
|
if (group_exists === false) {
|
|
debug.log('SNAPCAST','Creating new group',data.result.server.groups[i].id);
|
|
var g = new snapcastGroup();
|
|
newgroups.push(g);
|
|
g.initialise();
|
|
g.update(i, data.result.server.groups[i]);
|
|
} else {
|
|
debug.trace('SNAPCAST','Updating group',data.result.server.groups[i].id);
|
|
newgroups.push(groups[group_exists]);
|
|
groups[group_exists].update(i, data.result.server.groups[i]);
|
|
}
|
|
}
|
|
// Find removed groups
|
|
for (var i in groups) {
|
|
if (findGroup(newgroups, groups[i].getId()) === false) {
|
|
debug.log('SNAPCAST','Group',groups[i].getId(),'has been removed');
|
|
groups[i].removeSelf();
|
|
groups[i] = null;
|
|
}
|
|
}
|
|
groups = newgroups;
|
|
} else {
|
|
debug.shout('SNAPCAST','Got response with id',data.id,'expecting',id);
|
|
}
|
|
updatetimer = setTimeout(snapcast.updateStatus, 60000);
|
|
},
|
|
|
|
streamInfo: function(streamid) {
|
|
for (var i in streams) {
|
|
if (streams[i].id == streamid) {
|
|
var h = streams[i].status.capitalize()+' ';
|
|
h += streams[i].meta.STREAM;
|
|
h += ' <span class="snapclienthost">('+streams[i].uri.query.codec+' '+streams[i].uri.query.sampleformat+')</span>';
|
|
return h;
|
|
}
|
|
}
|
|
return 'Unknown Stream';
|
|
},
|
|
|
|
getAllStreams: function() {
|
|
return streams;
|
|
},
|
|
|
|
getAllGroups: function() {
|
|
var retval = new Array();
|
|
for (var i in groups) {
|
|
retval.push(groups[i].getInfo());
|
|
}
|
|
return retval;
|
|
},
|
|
|
|
setClientVolume: function(id, volume, muted) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Client.SetVolume",
|
|
params: {
|
|
id: id,
|
|
volume: {
|
|
muted: muted,
|
|
percent: volume
|
|
}
|
|
}
|
|
}, snapcast.updateStatus);
|
|
},
|
|
|
|
setGroupMute: function(id, muted) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Group.SetMute",
|
|
params: {
|
|
id: id,
|
|
mute: muted
|
|
}
|
|
}, snapcast.updateStatus);
|
|
},
|
|
|
|
deleteClient: function(id) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Server.DeleteClient",
|
|
params: {
|
|
id: id
|
|
}
|
|
}, snapcast.gotStatus);
|
|
},
|
|
|
|
setClientName: function(id, name) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Client.SetName",
|
|
params: {
|
|
id: id,
|
|
name: name
|
|
}
|
|
}, snapcast.updateStatus);
|
|
},
|
|
|
|
setClientLatency: function(id, latency) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Client.SetLatency",
|
|
params: {
|
|
id: id,
|
|
latency: parseInt(latency)
|
|
}
|
|
}, snapcast.updateStatus);
|
|
},
|
|
|
|
setStream: function(group, stream) {
|
|
clearTimeout(updatetimer);
|
|
snapcastRequest({
|
|
method: "Group.SetStream",
|
|
params: {
|
|
id: group,
|
|
stream_id: stream
|
|
}
|
|
}, snapcast.updateStatus);
|
|
},
|
|
|
|
addClientToGroup: function(client, group) {
|
|
clearTimeout(updatetimer);
|
|
var groupindex = findGroup(groups, group);
|
|
var clients = groups[groupindex].getClients();
|
|
clients.push(client);
|
|
snapcastRequest({
|
|
method: "Group.SetClients",
|
|
params: {
|
|
clients: clients,
|
|
id: group
|
|
}
|
|
}, snapcast.gotStatus);
|
|
},
|
|
|
|
clearEverything: function() {
|
|
clearTimeout(updatetimer);
|
|
streams = [];
|
|
groups = [];
|
|
$('#snapcastgroups').empty();
|
|
}
|
|
}
|
|
|
|
}();
|
|
|
|
function snapcastGroup() {
|
|
|
|
var self = this;
|
|
var clients = new Array();
|
|
var holder;
|
|
var id = '';
|
|
var muted;
|
|
var streammenu;
|
|
var ind;
|
|
var name;
|
|
|
|
function findClient(arr, id) {
|
|
for (var i in arr) {
|
|
if (arr[i].getId() == id) {
|
|
return i;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this.initialise = function() {
|
|
holder = $('<div>', {class: 'snapcastgroup fullwidth'}).appendTo('#snapcastgroups');
|
|
var title = $('<div>', {class: 'containerbox snapgrouptitle dropdown-container'}).appendTo(holder);
|
|
title.append('<div class="fixed tag" name="groupname"></div>');
|
|
title.append('<div class="expand tag textcentre" name="groupstream"></div>');
|
|
var m = $('<i>', {class: "podicon fixed icon-menu clickicon"}).appendTo(title).on('click', self.setStream);
|
|
m = $('<i>', {class: "podicon fixed clickicon", name: "groupmuted"}).appendTo(title).on('click', self.setMute);
|
|
streammenu = $('<div>', {class: 'toggledown invisible'}).insertAfter(title);
|
|
}
|
|
|
|
this.removeSelf = function() {
|
|
holder.remove();
|
|
}
|
|
|
|
this.getId = function() {
|
|
return id;
|
|
}
|
|
|
|
this.update = function(index, data) {
|
|
ind = index;
|
|
id = data.id;
|
|
muted = data.muted;
|
|
name = data.name;
|
|
var g = language.gettext('snapcast_group')+index;
|
|
if (data.name) {
|
|
g += ' ('+data.name+')';
|
|
}
|
|
holder.find('div[name="groupname"]').html(g);
|
|
holder.find('div[name="groupstream"]').html(snapcast.streamInfo(data.stream_id));
|
|
var icon = data.muted ? 'icon-output-mute' : 'icon-output';
|
|
holder.find('i[name="groupmuted"]').removeClass('icon-output icon-output-mute').addClass(icon);
|
|
|
|
var newclients = new Array();
|
|
for (var i in data.clients) {
|
|
var client_exists = findClient(clients, data.clients[i].id);
|
|
if (client_exists === false) {
|
|
debug.log('SNAPCAST','Creating new client',data.clients[i].id);
|
|
var g = new snapcastClient();
|
|
newclients.push(g);
|
|
g.initialise(holder);
|
|
g.update(id, data.clients[i]);
|
|
} else {
|
|
debug.trace('SNAPCAST','Updating client',data.clients[i].id);
|
|
newclients.push(clients[client_exists]);
|
|
clients[client_exists].update(id, data.clients[i]);
|
|
}
|
|
}
|
|
// Find removed groups
|
|
for (var i in clients) {
|
|
if (findClient(newclients, clients[i].getId()) === false) {
|
|
debug.log('SNAPCAST','Client',clients[i].getId(),'has been removed');
|
|
clients[i].removeSelf();
|
|
clients[i] = null;
|
|
}
|
|
}
|
|
clients = newclients;
|
|
}
|
|
|
|
this.setMute = function() {
|
|
snapcast.setGroupMute(id, !muted);
|
|
}
|
|
|
|
this.setStream = function() {
|
|
streammenu.empty();
|
|
var s = snapcast.getAllStreams();
|
|
var a = $('<div>', {class: 'menuitem textcentre'}).appendTo(streammenu);
|
|
a.html('Change Stream');
|
|
for (var i in s) {
|
|
var d = $('<div>', {class: 'backhi clickitem', name: s[i].id}).appendTo(streammenu).on('click', self.changeStream);
|
|
d.html(snapcast.streamInfo(s[i].id));
|
|
}
|
|
streammenu.slideToggle('fast');
|
|
}
|
|
|
|
this.changeStream = function(e) {
|
|
var streamid = $(this).attr('name');
|
|
debug.log('SNAPCAST', 'Group',id,'changing stream to',streamid);
|
|
snapcast.setStream(id, streamid);
|
|
streammenu.slideToggle('fast');
|
|
}
|
|
|
|
this.getInfo = function() {
|
|
return {index: ind, id: id, name: name};
|
|
}
|
|
|
|
this.getClients = function() {
|
|
var retval = new Array();
|
|
for (var i in clients) {
|
|
retval.push(clients[i].getId());
|
|
}
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
function snapcastClient() {
|
|
|
|
var self = this;
|
|
var holder;
|
|
var id = '';
|
|
var volume;
|
|
var volumepc;
|
|
var muted;
|
|
var vc;
|
|
var groupmenu;
|
|
var grouplist;
|
|
var groupid;
|
|
var connected;
|
|
|
|
this.initialise = function(parentdiv) {
|
|
holder = $('<div>', {class: 'snapcastclient'}).appendTo(parentdiv);
|
|
var title = $('<div>', {class: 'containerbox dropdown-container'}).appendTo(holder);
|
|
var n = $('<input>', {type: "text", class: "expand tag snapclientname", name: "clientname"}).appendTo(title);
|
|
var but = $('<button>', {class: 'fixed'}).appendTo(title).html(language.gettext('label_update')).on('click', self.changeName);
|
|
var client = $('<div>', {class: 'containerbox'}).appendTo(holder);
|
|
client.append('<div class="expand snapclienthost" name="clienthost"></div>');
|
|
var m = $('<i>', {class: "podicon fixed icon-menu clickicon"}).appendTo(title).on('click', self.setGroup);
|
|
var rb = $('<i>', {class: "fixed podicon icon-cancel-circled clickicon"}).appendTo(title).on('click', self.deleteClient);
|
|
vc = $('<div>', {class: 'containerbox dropdown-container invisible'}).appendTo(holder);
|
|
volume = $('<div>', {class: 'expand snapclienthost'}).appendTo(vc);
|
|
var m = $('<i>', {class: "podicon fixed clickicon", name :"clientmuted"}).appendTo(vc).on('click', self.setMute);
|
|
volume.volumeControl({
|
|
orientation: 'horizontal',
|
|
command: self.setVolume
|
|
});
|
|
groupmenu = $('<div>', {class: 'toggledown invisible'}).insertAfter(title);
|
|
var j = $('<div>', {class: "containerbox dropdown-container"}).appendTo(groupmenu);
|
|
var k = $('<div>', {class: "expand"}).appendTo(j);
|
|
k = $('<div>', {class: 'fixed padright'}).appendTo(j).html(language.gettext('snapcast_latency'));
|
|
k = $('<input>', {type: 'text', class: 'fixed', name: "latency", size: "6", style: "width:6em"}).appendTo(j);
|
|
k = $('<button>', {class: "fixed"}).appendTo(j).html(language.gettext('snapcast_setlatency')).on("click", self.changeLatency);
|
|
grouplist = $('<div>').appendTo(groupmenu);
|
|
}
|
|
|
|
this.removeSelf = function() {
|
|
holder.remove();
|
|
}
|
|
|
|
this.getId = function() {
|
|
return id;
|
|
}
|
|
|
|
this.update = function(index, data) {
|
|
groupid = index;
|
|
id = data.id;
|
|
muted = data.config.volume.muted;
|
|
volumepc = data.config.volume.percent;
|
|
connected = data.connected;
|
|
var g = '';
|
|
if (data.config.name) {
|
|
g = data.config.name;
|
|
} else {
|
|
g = id;
|
|
}
|
|
holder.find('input[name="clientname"]').val(g);
|
|
if (data.connected) {
|
|
holder.find('div[name="clienthost"]').html('('+data.host.name+' - '+data.host.os+')');
|
|
volume.volumeControl("displayVolume", data.config.volume.percent);
|
|
var icon = data.config.volume.muted ? 'icon-output-mute' : 'icon-output';
|
|
holder.find('i[name="clientmuted"]').removeClass('icon-output icon-output-mute').addClass(icon);
|
|
holder.find('input[name="latency"]').val(data.config.latency);
|
|
vc.removeClass('invisible');
|
|
} else {
|
|
holder.find('div[name="clienthost"]').html('<span class="tag">'+language.gettext('snapcast_notconnected')+'</span>');
|
|
if (!vc.hasClass('invisible')) {
|
|
vc.addClass('invisible');
|
|
}
|
|
}
|
|
}
|
|
|
|
this.setVolume = function(v) {
|
|
v = Math.round(v);
|
|
debug.log('SNAPCAST','Client',id,'setting volume to',v);
|
|
snapcast.setClientVolume(id, v, muted);
|
|
}
|
|
|
|
this.setMute = function() {
|
|
debug.log('SNAPCAST','Client',id,'toggling mute state');
|
|
snapcast.setClientVolume(id, volumepc, !muted);
|
|
}
|
|
|
|
this.deleteClient = function() {
|
|
snapcast.deleteClient(id);
|
|
}
|
|
|
|
this.changeName = function() {
|
|
snapcast.setClientName(id, holder.find('input[name="clientname"]').val());
|
|
}
|
|
|
|
this.changeLatency = function() {
|
|
snapcast.setClientLatency(id, holder.find('input[name="latency"]').val());
|
|
}
|
|
|
|
this.setGroup = function() {
|
|
if (connected) {
|
|
grouplist.empty();
|
|
var g = snapcast.getAllGroups();
|
|
if (g.length > 1) {
|
|
var a = $('<div>', {class: 'menuitem textcentre'}).appendTo(grouplist);
|
|
a.html(language.gettext('snapcast_changegroup'));
|
|
for (var i in g) {
|
|
if (g[i].id != groupid) {
|
|
var d = $('<div>', {class: 'backhi clickitem', name: g[i].id}).appendTo(grouplist).on('click', self.changeGroup);
|
|
var h = language.gettext('snapcast_group')+g[i].index;
|
|
if (g[i].name) {
|
|
h += ' ('+g[i].name+')';
|
|
}
|
|
d.html(h);
|
|
}
|
|
}
|
|
}
|
|
groupmenu.slideToggle('fast');
|
|
}
|
|
}
|
|
|
|
this.changeGroup = function(e) {
|
|
var groupid = $(this).attr('name');
|
|
debug.log('SNAPCAST', 'Client',id,'changing group to',groupid);
|
|
snapcast.addClientToGroup(id, groupid);
|
|
groupmenu.slideToggle('fast');
|
|
}
|
|
|
|
} |