Skip to content

Commit

Permalink
Improve favorite support; push index to Miniserver
Browse files Browse the repository at this point in the history
  • Loading branch information
mjesun committed May 23, 2020
1 parent e6a5bfd commit daf98ff
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 42 deletions.
123 changes: 83 additions & 40 deletions bin/service/music-server/index.js
Expand Up @@ -18,6 +18,14 @@ const errors = {
5: 'BACKEND_ERROR',
};

const BASE_DELTA = 1000000;

const BASE_FAVORITE_ZONE = 0 * BASE_DELTA;
const BASE_FAVORITE_GLOBAL = 1 * BASE_DELTA;
const BASE_PLAYLIST = 2 * BASE_DELTA;
const BASE_LIBRARY = 3 * BASE_DELTA;
const BASE_INPUT = 4 * BASE_DELTA;

module.exports = class MusicServer {
constructor(config) {
const zones = [];
Expand Down Expand Up @@ -81,8 +89,9 @@ module.exports = class MusicServer {

connection.send('LWSS V 2.3.9.2 | ~API:1.6~');

this._pushAudioSyncEvents(this._zones);
this._pushAudioEvents(this._zones);
this._pushAudioSyncEvents(this._zones);
this._pushRoomFavEvents(this._zones);
});

httpServer.listen(this._config.port);
Expand Down Expand Up @@ -152,6 +161,10 @@ module.exports = class MusicServer {
this._pushAudioSyncEvents([zone]);
}

pushRoomFavEvent(zone) {
this._pushRoomFavEvents([zone]);
}

pushQueueEvent(zone) {
this._pushQueueEvents([zone]);
}
Expand Down Expand Up @@ -184,12 +197,13 @@ module.exports = class MusicServer {
});
}

_pushQueueEvents(zones) {
_pushRoomFavEvents(zones) {
zones.forEach((zone) => {
const message = JSON.stringify({
audio_queue_event: [
roomfav_event: [
{
playerid: this._zones.indexOf(zone) + 1,
'playerid': this._zones.indexOf(zone) + 1,
'playing slot': zone.getFavoriteId(),
},
],
});
Expand All @@ -216,6 +230,22 @@ module.exports = class MusicServer {
});
}

_pushQueueEvents(zones) {
zones.forEach((zone) => {
const message = JSON.stringify({
audio_queue_event: [
{
playerid: this._zones.indexOf(zone) + 1,
},
],
});

this._wsConnections.forEach((connection) => {
connection.send(message);
});
});
}

_handler(method) {
const index = method.indexOf('?');
const url = index === -1 ? method : method.substr(0, index);
Expand Down Expand Up @@ -278,6 +308,9 @@ module.exports = class MusicServer {
):
return this._audioAlarm(url);

case /(?:^|\/)audio\/\d+\/favoriteplay(?:\/|$)/.test(url):
return this._audioFavoritePlay(url, []);

case /(?:^|\/)audio\/\d+\/getqueue(?:\/|$)/.test(url):
return this._audioGetQueue(url, []);

Expand Down Expand Up @@ -391,7 +424,7 @@ module.exports = class MusicServer {
{
totalitems: total,
start: +start,
items: items.map(this._convert(5, +start)),
items: items.map(this._convert(5, BASE_FAVORITE_GLOBAL, +start)),
},
]);
}
Expand All @@ -402,8 +435,8 @@ module.exports = class MusicServer {
return this._response(
url,
'getinputs',
items.map((item) => ({
id: this._encodeId(item.id),
items.map((item, i) => ({
id: this._encodeId(item.id, BASE_INPUT + i),
name: item.title,
enabled: true,
})),
Expand All @@ -419,7 +452,7 @@ module.exports = class MusicServer {
id: +requestId,
totalitems: total,
start: +start,
items: items.map(this._convert(2, +start)),
items: items.map(this._convert(2, BASE_LIBRARY, +start)),
},
]);
}
Expand All @@ -445,7 +478,7 @@ module.exports = class MusicServer {
id: +requestId,
totalitems: total,
start: +start,
items: items.map(this._convert(3, +start)),
items: items.map(this._convert(3, BASE_PLAYLIST, +start)),
},
]);
}
Expand All @@ -462,14 +495,24 @@ module.exports = class MusicServer {
id: +zoneId,
totalitems: total,
start: +start,
items: items.map(this._convert(4, +start)),
items: items.map(this._convert(4, BASE_FAVORITE_ZONE, +start)),
},
]);
}

return this._response(url, 'getroomfavs', []);
}

async _audioFavoritePlay(url) {
const [, zoneId, , id] = url.split('/');
const zone = this._zones[+zoneId - 1];
const [decodedId, position] = this._decodeId(id);

await zone.play(decodedId, position);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}

_audioCfgGetSyncedPlayers(url) {
return this._emptyCommand(url, []);
}
Expand Down Expand Up @@ -522,19 +565,12 @@ module.exports = class MusicServer {
total = 1;
}

console.log({
id: +zoneId,
totalitems: total,
start: +start,
items: items.map(this._convert(2, +start)),
});

return this._response(url, 'getqueue', [
{
id: +zoneId,
totalitems: total,
start: +start,
items: items.map(this._convert(2, +start)),
items: items.map(this._convert(2, 0, +start)),
},
]);
}
Expand All @@ -552,17 +588,19 @@ module.exports = class MusicServer {
async _audioLibraryPlay(url) {
const [, zoneId, , , id] = url.split('/');
const zone = this._zones[+zoneId - 1];
const [decodedId, position] = this._decodeId(id);

zone.play(this._decodeId(id));
await zone.play(decodedId, position);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}

async _audioLineIn(url) {
const [, zoneId, id] = url.split('/');
const zone = this._zones[+zoneId - 1];
const [decodedId, position] = this._decodeId(id.replace(/^linein/, ''));

zone.play(this._decodeId(id.replace(/^linein/, '')));
await zone.play(decodedId, position);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}
Expand All @@ -576,24 +614,25 @@ module.exports = class MusicServer {
return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}

_audioPlay(url) {
async _audioPlay(url) {
const [, zoneId] = url.split('/');
const zone = this._zones[+zoneId - 1];

if (zone.getMode() === 'stop') {
zone.play('');
await zone.play(null, -1);
} else {
zone.resume();
await zone.resume();
}

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}

_audioPlaylist(url) {
async _audioPlaylist(url) {
const [, zoneId, , , id] = url.split('/');
const zone = this._zones[+zoneId - 1];
const [decodedId, position] = this._decodeId(id);

zone.play(this._decodeId(id));
await zone.play(decodedId, position);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}
Expand Down Expand Up @@ -656,15 +695,17 @@ module.exports = class MusicServer {
const favorites = await zone.getFavoritesList().get(+position - 1, 1);
const id = favorites.items[0].id;

await zone.play(id);
await zone.play(id, +position - 1);

this._pushRoomFavEvents([zone]);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}

async _audioRoomFavSavePath(url) {
const [, zoneId, , , position, id, title] = url.split('/');
const zone = this._zones[+zoneId - 1];
const decodedId = this._decodeId(id);
const [decodedId] = this._decodeId(id);

const item = {
id: decodedId,
Expand All @@ -678,11 +719,11 @@ module.exports = class MusicServer {
return this._emptyCommand(url, []);
}

_audioServicePlay(url) {
async _audioServicePlay(url) {
const [, zoneId, , , , id] = url.split('/');
const zone = this._zones[+zoneId - 1];
const [decodedId, position] = this._decodeId(id);

zone.play(this._decodeId(id));
await zone.play(decodedId, position);

return this._audioCfgGetPlayersDetails('audio/cfg/getplayersdetails');
}
Expand Down Expand Up @@ -736,7 +777,7 @@ module.exports = class MusicServer {
playerid: playerId,
album: track.album,
artist: track.artist,
audiopath: this._encodeId(track.id),
audiopath: this._encodeId(track.id, 0),
audiotype: 2,
coverurl: track.image || '',
duration: mode === 'buffer' ? 0 : Math.ceil(track.duration / 1000),
Expand All @@ -752,7 +793,7 @@ module.exports = class MusicServer {
};
}

_convert(type, start) {
_convert(type, base, start) {
return (item, i) => {
if (!item) {
return {
Expand All @@ -770,15 +811,15 @@ module.exports = class MusicServer {
type,
slot: start + i + 1,
qindex: +start + i + 1,
audiopath: this._encodeId(item.id),
audiopath: this._encodeId(item.id, base + i),
coverurl: item.image || undefined,
id: this._encodeId(item.id),
id: this._encodeId(item.id, base + i),
name: item.title,
};
};
}

_encodeId(data) {
_encodeId(data, offset) {
const table = {
'+': '-',
'/': '_',
Expand All @@ -793,7 +834,7 @@ module.exports = class MusicServer {
);
}

return Buffer.from('' + data)
return Buffer.from(JSON.stringify([data, offset]))
.toString('base64')
.replace(/[+/=]/g, (str) => table[str]);
}
Expand All @@ -804,10 +845,12 @@ module.exports = class MusicServer {
'_': '/',
};

return Buffer.from(
data.replace(/[-_]/g, (str) => table[str]),
'base64',
).toString();
return JSON.parse(
Buffer.from(
data.replace(/[-_]/g, (str) => table[str]),
'base64',
),
);
}

_response(url, name, result) {
Expand Down
24 changes: 22 additions & 2 deletions bin/service/music-server/music-zone.js
Expand Up @@ -10,6 +10,8 @@ module.exports = class MusicZone {
this._updateId = undefined;
this._updateTime = NaN;

this._favoriteId = -1;

this._player = {
id: '',
mode: 'stop',
Expand All @@ -27,6 +29,10 @@ module.exports = class MusicZone {
this._getState();
}

getFavoriteId() {
return this._favoriteId;
}

getFavoritesList() {
return this._favorites;
}
Expand Down Expand Up @@ -85,9 +91,11 @@ module.exports = class MusicZone {
}
}

async play(id) {
async play(id, favoriteId) {
const transaction = this._transaction();

this._favoriteId = favoriteId;

this._track = this._getEmptyTrack();
this._player.time = 0;
this._setMode('buffer');
Expand All @@ -97,7 +105,7 @@ module.exports = class MusicZone {
try {
await this._sendPlayerCommand(
'POST',
id ? '/play/' + encodeURIComponent(id) : '/play',
id === null ? '/play' : '/play/' + encodeURIComponent(id),
);
} catch (err) {
if (err.type === 'BACKEND_ERROR') {
Expand Down Expand Up @@ -334,6 +342,17 @@ module.exports = class MusicZone {
}
}

_pushRoomFavEvent() {
if (!this._roomFavEventSent) {
this._roomFavEventSent = true;

setTimeout(() => {
this._musicServer.pushRoomFavEvent(this);
this._roomFavEventSent = false;
}, 25);
}
}

async _sendPlayerCommand(method, url, body) {
const data = await this._musicServer.call(method, this._url() + url, body);
const track = data.track || this._getEmptyTrack();
Expand All @@ -350,6 +369,7 @@ module.exports = class MusicZone {
}

this._pushAudioEvent();
this._pushRoomFavEvent();
}

_transaction() {
Expand Down

0 comments on commit daf98ff

Please sign in to comment.