Skip to content

Commit

Permalink
Merge pull request #509 from pascalopitz/alpha
Browse files Browse the repository at this point in the history
Backported master updates to Alpha, did some dependency upgrade chores
  • Loading branch information
svrooij authored Mar 18, 2021
2 parents 692ea17 + 56c5fa5 commit 725d663
Show file tree
Hide file tree
Showing 18 changed files with 1,442 additions and 6,925 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
release:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/alpha' # only run on alpha branch for now.
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/alpha'
steps:
- name: Checkout
uses: actions/checkout@v1
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ node_modules
typings

# VS
.vscode
.vscode/*.json
!.vscode/launch.json
config/*local.json

# Ignore Mac DS_Store files
Expand Down
17 changes: 0 additions & 17 deletions .travis.yml

This file was deleted.

21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch current example",
"program": "${file}",
"skipFiles": [
"<node_internals>/**"
],
"env": {
"DEBUG":"sonos:*",
"SONOS_HOST":"192.168.96.56"
},
}
]
}
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"
alt="Standard Codestyle">
</a>
<a href="https://discord.gg/m62rFAV4NU">
<img src="https://img.shields.io/discord/782374564054564875" alt="Discord badge" />
</a>
</p>

**node-sonos** gives you the power to control all your Sonos devices from your own apps in JavaScript. Automatically discover your devices on the network and control the playback and queue with instant events announcing change.
Expand Down Expand Up @@ -73,8 +76,7 @@ DeviceDiscovery().once('DeviceAvailable', (device) => {
console.log('found device at ' + device.host)

// get all groups
sonos = new Sonos(device.host)
sonos.getAllGroups().then(groups => {
device.getAllGroups().then(groups => {
groups.forEach(group => {
console.log(group.Name);
})
Expand Down Expand Up @@ -300,7 +302,9 @@ If you want to contribute something check out these ['help-wanted' issues](https

## Questions

Do you have a question about this library, we are glad to help you [Ask Question](https://github.com/bencevans/node-sonos/issues/new?title=Question%3A%20%3CYour%20text%3E&body=%23%20Question%0A&labels=question&assignee=svrooij). You can see all questions [here](https://github.com/bencevans/node-sonos/issues?utf8=%E2%9C%93&q=label%3Aquestion+)
[![Join us on Discord][badge_discord]][link_discord]

The best place to ask your questions is in Discord, we are there to help you. [Join us on Discord][link_discord].

### Unsupported sonos features

Expand Down Expand Up @@ -344,3 +348,6 @@ git push origin
## Licence

MIT © [Ben Evans](https://bencevans.io)

[badge_discord]: https://img.shields.io/discord/782374564054564875
[link_discord]: https://discord.gg/m62rFAV4NU
14 changes: 7 additions & 7 deletions examples/nowplayingwebsite.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
var http = require('http')
var Sonos = require('../').Sonos
const http = require('http')
const Sonos = require('../').Sonos

var sonos = new Sonos(process.env.SONOS_HOST || '192.168.2.11')
const sonos = new Sonos(process.env.SONOS_HOST || '192.168.2.11')

var server = http.createServer(async function (req, res) {
var track = await sonos.currentTrack()
const server = http.createServer(async function (req, res) {
const track = await sonos.currentTrack()
res.writeHead(200, {
'Content-Type': 'text/html'
})

var rows = []
const rows = []

for (var key in track) {
for (const key in track) {
if (key === 'albumArtURL') {
rows.push('<tr><th>' + key + '</th><td><img src="' + track[key] + '"/></td></tr>')
} else {
Expand Down
2 changes: 1 addition & 1 deletion examples/playSpotifyMusic.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ sonos.setSpotifyRegion(Regions.EU)
// your Sonos system.

// var spotifyUri = 'spotify:artistTopTracks:72qVrKXRp9GeFQOesj0Pmv'
var spotifyUri = 'spotify:track:6sYJuVcEu4gFHmeTLdHzRz'
const spotifyUri = 'spotify:track:6sYJuVcEu4gFHmeTLdHzRz'

sonos.play(spotifyUri)
.then(success => {
Expand Down
4 changes: 2 additions & 2 deletions examples/searchinfo.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
var sonos = require('../index')
const sonos = require('../index')

console.log('\nSearching for Sonos devices on network...')

sonos.DeviceDiscovery(function (device, model) {
var devInfo = '\n'
let devInfo = '\n'
devInfo += 'Device \t' + JSON.stringify(device) + ' (' + model + ')\n'
device.getZoneAttrs(function (err, attrs) {
if (err) devInfo += '`- failed to retrieve zone attributes\n'
Expand Down
4 changes: 2 additions & 2 deletions examples/switchToLineIn.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ const sonos = new Sonos(process.env.SONOS_HOST || '192.168.96.223')
sonos.getZoneInfo().then(data => {
// console.log('Got zone info %j', data)
// The mac is needed for switch to a different channel
var macCleaned = data.MACAddress.replace(/:/g, '')
const macCleaned = data.MACAddress.replace(/:/g, '')
console.log('Cleaned mac %j', macCleaned)

// To swtich on a playbar do the following
var uri = 'x-sonos-htastream:RINCON_' + macCleaned + '01400:spdif'
const uri = 'x-sonos-htastream:RINCON_' + macCleaned + '01400:spdif'

// To switch on a Play:5 do the following
// var uri = 'x-ricon-stream:RINCON_' + macCleaned + '01400'
Expand Down
22 changes: 11 additions & 11 deletions lib/deviceDiscovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ const Sonos = require('./sonos').Sonos
* @class DeviceDiscovery
* @emits 'DeviceAvailable' on a Sonos Component Discovery
*/
var DeviceDiscovery = function DeviceDiscovery (options) {
var self = this
const DeviceDiscovery = function DeviceDiscovery (options) {
const self = this
self.foundSonosDevices = {}
self.onTimeout = function () {
clearTimeout(self.pollTimer)
}
var PLAYER_SEARCH = Buffer.from(['M-SEARCH * HTTP/1.1',
const PLAYER_SEARCH = Buffer.from(['M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: ssdp:discover',
'MX: 1',
'ST: urn:schemas-upnp-org:device:ZonePlayer:1'].join('\r\n'))
var sendDiscover = function () {
['239.255.255.250', '255.255.255.255'].map(function (addr) {
const sendDiscover = function () {
['239.255.255.250', '255.255.255.255'].forEach(function (addr) {
self.socket.send(PLAYER_SEARCH, 0, PLAYER_SEARCH.length, 1900, addr)
})
// Periodically send discover packet to find newly added devices
Expand All @@ -40,11 +40,11 @@ var DeviceDiscovery = function DeviceDiscovery (options) {
this.socket = dgram.createSocket('udp4', function (buffer, rinfo) {
buffer = buffer.toString()
if (buffer.match(/.+Sonos.+/)) {
var modelCheck = buffer.match(/SERVER.*\((.*)\)/)
var model = (modelCheck.length > 1 ? modelCheck[1] : null)
var addr = rinfo.address
const modelCheck = buffer.match(/SERVER.*\((.*)\)/)
const model = (modelCheck.length > 1 ? modelCheck[1] : null)
const addr = rinfo.address
if (!(addr in self.foundSonosDevices)) {
var sonos = self.foundSonosDevices[addr] = new Sonos(addr)
const sonos = self.foundSonosDevices[addr] = new Sonos(addr)
self.emit('DeviceAvailable', sonos, model)
}
}
Expand Down Expand Up @@ -92,14 +92,14 @@ DeviceDiscovery.prototype.destroy = function (callback) {
* @param {Function} listener Optional 'DeviceAvailable' listener (sonos)
* @return {DeviceDiscovery}
*/
var deviceDiscovery = function (options, listener) {
const deviceDiscovery = function (options, listener) {
if (typeof options === 'function') {
listener = options
options = null
}
options = options || {}
listener = listener || null
var search = new DeviceDiscovery(options)
const search = new DeviceDiscovery(options)
if (listener !== null) {
search.on('DeviceAvailable', listener)
}
Expand Down
4 changes: 2 additions & 2 deletions lib/events/adv-listener.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class SonosListener extends EventEmitter {
const cancel = function (s) {
return s.cancelAllSubscriptions()
}
var cancelAll = this._deviceSubscriptions.map(cancel)
const cancelAll = this._deviceSubscriptions.map(cancel)
return Promise.all(cancelAll)
} else {
return new Promise((resolve, reject) => { reject(new Error('Not listening')) })
Expand Down Expand Up @@ -342,7 +342,7 @@ class DeviceSubscription {
* @param {String} timeout TimeOut header
*/
headerToDateTime (timeout) {
var seconds
let seconds

if ((!!timeout) && (timeout.indexOf('Second-') === 0)) timeout = timeout.substr(7)
seconds = (((!!timeout) && (!isNaN(timeout))) ? parseInt(timeout, 10) : 3600) - 15
Expand Down
33 changes: 21 additions & 12 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const parseString = require('xml2js').parseString
* Helper class
* @class Helpers
*/
var Helpers = {}
const Helpers = {}

/**
* Wrap in UPnP Envelope
Expand Down Expand Up @@ -87,12 +87,12 @@ Helpers.GenerateCustomMetadata = function (streamUrl, itemId, duration = '00:00:
* @return {Object} { uri: uri, metadata: metadata }
*/
Helpers.GenerateLocalMetadata = function (uri, artUri = '') {
var title = ''
var match = /.*\/(.*)$/g.exec(uri.replace(/\.[a-zA-Z0-9]{3}$/, ''))
let title = ''
const match = /.*\/(.*)$/g.exec(uri.replace(/\.[a-zA-Z0-9]{3}$/, ''))
if (match) {
title = match[1]
}
var meta = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="##ITEMID##" parentID="##PARENTID##" restricted="true"><dc:title>##RESOURCETITLE##</dc:title><upnp:class>##UPNPCLASS##</upnp:class><upnp:albumArtURI>##ARTURI##</upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">##REGION##</desc></item></DIDL-Lite>'
const meta = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="##ITEMID##" parentID="##PARENTID##" restricted="true"><dc:title>##RESOURCETITLE##</dc:title><upnp:class>##UPNPCLASS##</upnp:class><upnp:albumArtURI>##ARTURI##</upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">##REGION##</desc></item></DIDL-Lite>'
if (uri.startsWith('x-file-cifs')) {
return {
uri: uri,
Expand All @@ -105,8 +105,8 @@ Helpers.GenerateLocalMetadata = function (uri, artUri = '') {
}
}
if (uri.startsWith('x-rincon-playlist')) {
var parentMatch = /.*#(.*)\/.*/g.exec(uri)
var parentID = parentMatch[1]
const parentMatch = /.*#(.*)\/.*/g.exec(uri)
const parentID = parentMatch[1]

return {
uri: uri,
Expand All @@ -129,15 +129,15 @@ Helpers.GenerateLocalMetadata = function (uri, artUri = '') {
* @return {Object} options {uri: Spotify uri, metadata: metadata}
*/
Helpers.GenerateMetadata = function (uri, title = '', region = '3079') {
var parts = uri.split(':')
const parts = uri.split(':')
if (!((parts.length === 2 && (parts[0] === 'radio' || parts[0] === 'x-sonosapi-stream' || parts[0] === 'x-rincon-cpcontainer')) || (parts.length >= 3 && parts[0] === 'spotify'))) {
debug('Returning string because it isn\'t recognized')
return Helpers.GenerateLocalMetadata(uri)
}
var meta = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="##SPOTIFYURI##" ##PARENTID##restricted="true"><dc:title>##RESOURCETITLE##</dc:title><upnp:class>##SPOTIFYTYPE##</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">##REGION##</desc></item></DIDL-Lite>'
let meta = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="##SPOTIFYURI##" ##PARENTID##restricted="true"><dc:title>##RESOURCETITLE##</dc:title><upnp:class>##SPOTIFYTYPE##</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">##REGION##</desc></item></DIDL-Lite>'

if (parts[0] === 'radio' || parts[0] === 'x-sonosapi-stream') {
var radioTitle = title || 'TuneIn Radio'
const radioTitle = title || 'TuneIn Radio'
if (parts[0] === 'radio') {
return {
uri: 'x-sonosapi-stream:' + parts[1] + '?sid=254&flags=8224&sn=0',
Expand All @@ -161,7 +161,7 @@ Helpers.GenerateMetadata = function (uri, title = '', region = '3079') {
} else {
meta = meta.replace('##REGION##', 'SA_RINCON' + region + '_X_#Svc' + region + '-0-Token')
}
var spotifyUri = uri.replace(/:/g, '%3a')
const spotifyUri = uri.replace(/:/g, '%3a')

if (uri.startsWith('spotify:track:')) { // Just one track
return {
Expand All @@ -187,6 +187,14 @@ Helpers.GenerateMetadata = function (uri, title = '', region = '3079') {
.replace('##SPOTIFYTYPE##', 'object.container.playlistContainer')
.replace('##PARENTID##', '')
}
} else if (uri.startsWith('spotify:playlist:')) { // Playlist
return {
uri: 'x-rincon-cpcontainer:1006206c' + spotifyUri,
metadata: meta.replace('##SPOTIFYURI##', '1006206c' + spotifyUri)
.replace('##RESOURCETITLE##', '')
.replace('##SPOTIFYTYPE##', 'object.container.album.playlistContainer')
.replace('##PARENTID##', '')
}
} else if (uri.startsWith('spotify:user:')) {
return {
uri: 'x-rincon-cpcontainer:10062a6c' + spotifyUri + '?sid=9&flags=10860&sn=7',
Expand All @@ -196,8 +204,8 @@ Helpers.GenerateMetadata = function (uri, title = '', region = '3079') {
.replace('##PARENTID##', 'parentID="10082664playlists" ')
}
} else if (uri.startsWith('spotify:artistRadio:')) { // Artist radio
var spotifyTitle = title || 'Artist Radio'
var parentId = spotifyUri.replace('artistRadio', 'artist')
const spotifyTitle = title || 'Artist Radio'
const parentId = spotifyUri.replace('artistRadio', 'artist')
return {
uri: 'x-sonosapi-radio:' + spotifyUri + '?sid=12&flags=8300&sn=5',
metadata: meta.replace('##SPOTIFYURI##', '100c206c' + spotifyUri)
Expand Down Expand Up @@ -276,6 +284,7 @@ Helpers.ParseDIDLItem = function (item, host, port, trackUri) {
title: item['r:streamContent'] || item['dc:title'] || null,
artist: item['dc:creator'] || null,
album: item['upnp:album'] || null,
albumArtist: item['r:albumArtist'] || null,
albumArtURI
}
if (trackUri) track.uri = trackUri
Expand Down
8 changes: 5 additions & 3 deletions lib/services/AVTransport.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,14 @@ class AVTransport extends Service {
const trackUri = result.TrackURI || null
const queuePosition = parseInt(result.Track)
if (result.TrackMetaData && result.TrackMetaData !== 'NOT_IMPLEMENTED') { // There is some metadata, lets parse it.
var metadata = await Helpers.ParseXml(result.TrackMetaData)
const metadata = await Helpers.ParseXml(result.TrackMetaData)
const track = Helpers.ParseDIDL(metadata)
track.position = position
track.duration = duration
track.albumArtURL = !track.albumArtURI ? null
: track.albumArtURI.startsWith('http') ? track.albumArtURI
track.albumArtURL = !track.albumArtURI
? null
: track.albumArtURI.startsWith('http')
? track.albumArtURI
: 'http://' + this.host + ':' + this.port + track.albumArtURI
if (trackUri) track.uri = trackUri
track.queuePosition = queuePosition
Expand Down
2 changes: 1 addition & 1 deletion lib/services/Service.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class Service {
})
}
messageBody += `</u:${action}>`
var responseTag = `u:${action}Response`
const responseTag = `u:${action}Response`
request({
url: 'http://' + this.host + ':' + this.port + this.controlURL,
method: 'POST',
Expand Down
Loading

0 comments on commit 725d663

Please sign in to comment.