svrooij / sonos2mqtt

:speaker: Sonos mqtt controller. Control your speakers from your mqtt server. mqtt-smarthome insprired.
https://sonos2mqtt.svrooij.io/
MIT License
76 stars 18 forks source link

Other Streaming Services (Deezer/Apple Music/Amazon Music/...)? #128

Closed dirstel closed 3 years ago

dirstel commented 3 years ago

I tried hard to get my Sonos stuff play some music from a streamingservice different than Spotify - but had no success. Does anyone know, how to adress track/album/playlist on deezer/apple music/amazon music)?

When switching to a playlist from deezer via Sonos App, spotify is mentioned in the item ID- is this correct?:

payload.EnqueuedTransportURIMetaData: { "Artist":"[MyUsername]", "AlbumArtUri":"https://cdns-images.dzcdn.net/images/cover/[...].jpg", "Title":"[MyPlaylistTitle]", "UpnpClass":"object.container.playlistContainer.#DEFAULT", "ItemId":"10060a6cplaylist_spotify%3aplaylist-5299030562", "ParentId":"10fe2664playlists" }

Is any other service supported?

Sample favorite album from deezer: {"AlbumArtUri":"http://e-cdn-images.deezer.com/images/cover/135571ae1243feba64f4858fe90e11c3/250x250-000000-80-0-0.jpg","Title":"All For You (Originals International Version)","UpnpClass":"object.itemobject.item.sonos-favorite","ItemId":"FV:2/66","ParentId":"FV:2","TrackUri":"x-rincon-cpcontainer:1004006calbum-119589?sid=2&flags=108&sn=3","ProtocolInfo":"x-rincon-cpcontainer:*:*:*"}

Sample favorite playlist from deezer: {"AlbumArtUri":"http://e-cdn-images.deezer.com/images/playlist/2ecfca5836a2249f9669973fa2af4a3f/250x250-000000-80-0-0.jpg","Title":"Café del Mar Ibiza","UpnpClass":"object.itemobject.item.sonos-favorite","ItemId":"FV:2/113","ParentId":"FV:2","TrackUri":"x-rincon-cpcontainer:1006006cplaylist_spotify:playlist-4045469642?sid=2&flags=108&sn=16","ProtocolInfo":"x-rincon-cpcontainer:*:*:*"}

svrooij commented 3 years ago

@dirstel playing songs from music services need MetaData the main sonos library has a metadata helper.

It guesses the metadata based om the track uri. Specifically this method needs to be extended. Check out the Spotify code to find out what you need, the needed values are probably in the metadata of the status info in mqtt.

you can send a PR for support for the other music services. If you cannot get it to work, join us on discord and I might be able to help out with changing the code. (Since I don’t have an account for all those services).

dirstel commented 3 years ago

Thanks for quick response, showing me I'm not completly nuts :)

I'll try to dig in deeper in this topic. Having a quick glance at the methode you pointed me to, gives me some hint about amazon my work. so I'll try that first (to give my little ones quick access). but I definatly keep looking after deezer.

svrooij commented 3 years ago

The regular play this commands do automatic metadata generation. If you want to send the metadata yourself you can try to use the advanced command which allows you to also set the metadata, like sending the following to sonos/uuid_of_speaker/control:

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.SetAVTransportURI",
    "val": {
      "InstanceID": 0,
      "CurrentURI": "your-track-uri",
      "CurrentURIMetaData": {
        "UpnpClass": "", // this is important
        "CdUdn": "", // this is needed sometimes
        "ItemId": "", // this is needed sometimes for display purposes
        "ParentId": "", // this is needed sometimes to allow browse back up
      }
    }
  }
}

If you find the correct value for some track uri, you can either create a PR on the node-sonos-ts library or send them here and I'll add them. (For me it's hard to test since I don't have an account.)

dirstel commented 3 years ago

I figured out, how to load a Sonos-Playlist:

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.AddURIToQueue",
    "val": {
      "InstanceID": 0,
      "DesiredFirstTrackNumberEnqueued": 0,
      "EnqueueAsNext": true,
      "EnqueuedURI":"file:///jffs/settings/savedqueues.rsq#7",
      "EnqueuedURIMetaData": {
        "UpnpClass": "object.container.playlistContainer",
        "ItemId": "SQ:7",
        "CdUdn": "RINCON_AssociatedZPUDN"
      }
    }
  }
}

The Playlist is identified by a Number (in the example 7) which is used two times (EnqueuedURI and ItemId)

So the metadata-helper could be called with sth. like "sonos:playlist:" to load a given playlist.

dirstel commented 3 years ago

Loading a Deezer Album with ID 169734362

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.AddURIToQueue",
    "val": {
      "InstanceID": 0,
      "DesiredFirstTrackNumberEnqueued": 0,
      "EnqueueAsNext": true,
      "EnqueuedURI":"x-rincon-cpcontainer:1004006calbum-169734362?sid=2&flags=108&sn=23",
      "EnqueuedURIMetaData": {
        "UpnpClass": "object.container.album.musicAlbum.#HERO",
        "ItemId": "1004006calbum-169734362",
        "CdUdn": "SA_RINCON519_X_#Svc519-0-Token"
      }
    }
  }
}

Verified with two different albums.

dirstel commented 3 years ago

Loading a Deezer Playlist with ID 1371651955

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.AddURIToQueue",
    "val": {
      "InstanceID": 0,
      "DesiredFirstTrackNumberEnqueued": 0,
      "EnqueueAsNext": true,
      "EnqueuedURI":"x-rincon-cpcontainer:1006006cplaylist_spotify%3aplaylist-1371651955?sid=2&flags=108&sn=23",
      "EnqueuedURIMetaData": {
        "UpnpClass": "object.container.playlistContainer.#DEFAULT",
        "ItemId": "1006006cplaylist_spotify%3aplaylist-1371651955",
        "CdUdn": "SA_RINCON519_X_#Svc519-0-Token"
      }
    }
  }
}

Verified with two different and an additional private playlist.

dirstel commented 3 years ago

Loading Deezer artist top tracks with ID 6049784

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.AddURIToQueue",
    "val": {
      "InstanceID": 0,
      "DesiredFirstTrackNumberEnqueued": 0,
      "EnqueueAsNext": true,
      "EnqueuedURI":"x-rincon-cpcontainer:10fe206ctracks-artist-6049784?sid=2&flags=8300&sn=23",
      "EnqueuedURIMetaData": {
        "UpnpClass": "object.container.#DEFAULT",
        "ItemId": "10fe206ctracks-artist-6049784",
        "CdUdn": "SA_RINCON519_X_#Svc519-0-Token"
      }
    }
  }
}

Verified with two different artists.

dirstel commented 3 years ago

artist radio is not enqued, so this one needs some more investigation.

svrooij commented 3 years ago

artist radio is not enqued, so this one needs some more investigation.

That is probably a stream that needs to be played with SetTransport.... Because streams cannot be added to a queue

dirstel commented 3 years ago

queing a single track from deezer by id 1121931512

{
  "command": "adv-command",
  "input": {
    "cmd": "AVTransportService.AddURIToQueue",
    "val": {
      "InstanceID": 0,
      "DesiredFirstTrackNumberEnqueued": 0,
      "EnqueueAsNext": true,
      "EnqueuedURI":"x-sonos-http:tr%3a1121931512.mp3?sid=2&flags=8224&sn=23",
      "EnqueuedURIMetaData": {
        "UpnpClass": "object.item.audioItem.musicTrack.#DEFAULT",
        "ItemId": "10032020tr%3a1121931512",
        "CdUdn": "SA_RINCON519_X_#Svc519-0-Token"
      }
    }
  }
}

Verified with two different tracks.

dirstel commented 3 years ago

That is probably a stream that needs to be played with SetTransport.... Because streams cannot be added to a queue

seems so, the posted xml was quite large and I'd like to concentrate on the simple and simmilar actions :)

dirstel commented 3 years ago

Wrote this code based on your spotify code. It is not tested, as I'm "just" using a docker-image and it is hard for me to develop. But as it is "just" some string operations, it may work:

// Deezer
    if (parts.length === 3 && parts[0] === 'deezer') {
//      const deezerUri = trackUri.replace(/:/g, '%3a');
      const deezerUri = parts[2];
      track.Title = '';
      track.CdUdn = `SA_RINCON${deezerRegion}_X_#Svc${deezerRegion}-0-Token`; // Germany: SA_RINCON519_X_#Svc519-0-Token

      if (parts[1] === 'track') {
        track.TrackUri = `x-sonos-http:tr%3a${deezerUri}.mp3?sid=2&flags=8224&sn=23`;
        track.ItemId = `10032020tr%3a${deezerUri}`;
//        track.ParentId = ``; // ToDo: Parent navigation
        track.UpnpClass = 'object.item.audioItem.musicTrack.#DEFAULT';
        return track;
      }

      if (parts[1] === 'album') {
        track.TrackUri = `x-rincon-cpcontainer:1004006calbum-${deezerUri}?sid=2&flags=108&sn=23`;
        track.ItemId = `1004006calbum-${deezerUri}`;
//        track.ParentId = ``; // ToDo: Parent navigation
        track.UpnpClass = 'object.container.album.musicAlbum.#HERO';
        return track;
      }

//      if (parts[1] === 'artistRadio') {
//        track.TrackUri = `x-sonosapi-radio:${deezerUri}?sid=9&flags=8300&sn=7`;
//        track.ItemId = `100c206c${deezerUri}`;
//        track.Title = 'Artist radio';
//        track.UpnpClass = 'object.item.audioItem.audioBroadcast.#artistRadio';
//        track.ParentId = `10052064${deezerUri.replace('artistRadio', 'artist')}`;
//        return track;
//      }

      if (parts[1] === 'artistTopTracks') {
        track.TrackUri = `x-rincon-cpcontainer:10fe206ctracks-artist-{deezerUri}?sid=2&flags=8300&sn=23
        `;
        track.ItemId = `tracks-artist-{deezerUri}`;
//        track.ParentId = ``; // ToDo: Parent navigation
        track.UpnpClass = 'object.container.#DEFAULT';
        return track;
      }

      if (parts[1] === 'playlist') {
        track.TrackUri = `x-rincon-cpcontainer:1006006cplaylist_spotify%3aplaylist-${deezerUri}?sid=2&flags=108&sn=23`;
        track.ItemId = `1006006cplaylist_spotify%3aplaylist-${deezerUri}`;
//        track.Title = 'Spotify playlist';
//        track.ParentId = ``; // ToDo: Parent navigation
        track.UpnpClass = 'object.container.playlistContainer.#DEFAULT';
        return track;
      }

    }
github-actions[bot] commented 3 years ago

:tada: This issue has been resolved in version 3.1.1-beta.6 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

svrooij commented 3 years ago

@dirstel this automatic comment means you can try out the new functionality when you install the beta (svrooij/sonos2mqtt:beta) instead of the latest and you can try it out.

dirstel commented 3 years ago

@svrooij: I did try:

artistTopTrack did not work (tried enque and settransporturi). I'll investigate that next year ;) Thanks a lot!

github-actions[bot] commented 3 years ago

:tada: This issue has been resolved in version 3.1.2 :tada:

The release is available on:

Your semantic-release bot :package::rocket: