Roon Extension: Roon Web Controller v1.1.1

Happy days :grinning:

Works a charm.

Thoughts for consideration:

  • Any way to speed up/ cache list loading or to show progress? Caching would make it a lot snappier. Probably worthwhile caching subtrees in background ahead of user selection e.g. sub-genres.
  • Sort options would be useful e.g. Genres, Albums etc.

@Mike_Plugge running like a boss!
Thanks!

@evand +1!

@Mike_Plugge

It seems I’ve run into a bug…wasn’t doing anything in particular, in fact I’d left it running in Chrome on my Arch laptop and controlled proceedings using my phone whilst relaxing on the sofa.

Got back to find this in the terminal window:

<- REQUEST 2589 com.roonlabs.ping:1/ping -> COMPLETE 2589 Success <- CONTINUE 2 Changed {"zones_changed":[{"zone_id":"16014f7f3ca04ca1e3febdc3d1c8b939822b","display_name":"Mojo","outputs":[{"output_id":"17014f7f3ca04ca1e3febdc3d1c8b939822b","zone_id":"16014f7f3ca04ca1e3febdc3d1c8b939822b","can_group_with_output_ids":["17014f7f3ca04ca1e3febdc3d1c8b939822b"],"display_name":"Mojo","volume":{"type":"number","min":0,"max":100,"value":100,"step":1,"is_muted":true}}],"state":"playing","is_next_allowed":true,"is_previous_allowed":true,"is_pause_allowed":true,"is_play_allowed":false,"is_seek_allowed":true,"settings":{"loop":"disabled","shuffle":false,"auto_radio":true},"now_playing":{"seek_position":94,"length":359,"one_line":{"line1":"What's It All About - Aynsley Lister"},"two_line":{"line1":"What's It All About","line2":"Aynsley Lister"},"three_line":{"line1":"What's It All About","line2":"Aynsley Lister","line3":"Equilibrium"},"image_key":"03d70dbaa4a1a3c810e8a3dab4446269"}}]} <- CONTINUE 2 Changed {"zones_changed":[{"zone_id":"16014f7f3ca04ca1e3febdc3d1c8b939822b","display_name":"Mojo","outputs":[{"output_id":"17014f7f3ca04ca1e3febdc3d1c8b939822b","zone_id":"16014f7f3ca04ca1e3febdc3d1c8b939822b","can_group_with_output_ids":["17014f7f3ca04ca1e3febdc3d1c8b939822b"],"display_name":"Mojo","volume":{"type":"number","min":0,"max":100,"value":100,"step":1,"is_muted":true}}],"state":"playing","is_next_allowed":true,"is_previous_allowed":true,"is_pause_allowed":true,"is_play_allowed":false,"is_seek_allowed":true,"settings":{"loop":"disabled","shuffle":false,"auto_radio":true},"now_playing":{"seek_position":95,"length":359,"one_line":{"line1":"What's It All About - Aynsley Lister"},"two_line":{"line1":"What's It All About","line2":"Aynsley Lister"},"three_line":{"line1":"What's It All About","line2":"Aynsley Lister","line3":"Equilibrium"},"image_key":"03d70dbaa4a1a3c810e8a3dab4446269"}}]} ignoring malformed moo msg

In browser window it reported:

This extension is not enabled. Please use a Roon client to enable it.

Small bug… the background resets to default every so often.

Both great ideas. Sorting the Genre is something I can look into, but I do not think it is something I can implement. What is showed is what the API returns and it is based on the number of albums/artists in each of the Genres. It is the same order as in the official client. The order is dictated by the “item_key” value (see below). Sorting this would take operations on an intermediate array which could slow things down a bit. This would break further if there are more than 100 entries since the API always returns a maximum of 100 entries - even if the application requests another list size. Same is true for the Album sort.

There is a little bit of control over the Artist and Composer sort options in the “Settings” section - but those are both options that are provided by the API, so it controls what the server returns.

Unfortunately, caching is impractical based on what the Roon API provides. (I REALLY wanted to do caching, but couldn’t!)

For a better explanation, I will give you an example of a typical flow of data as returned by the API.

Click home:

[ { title: 'Library',
    subtitle: null,
    image_key: null,
    item_key: '1572:0' },
  { title: 'Playlists',
    subtitle: null,
    image_key: null,
    item_key: '1572:1' },
  { title: 'Internet Radio',
    subtitle: null,
    image_key: null,
    item_key: '1572:2' },
  { title: 'Genres',
    subtitle: null,
    image_key: null,
    item_key: '1572:3' },
  { title: 'Settings',
    subtitle: null,
    image_key: null,
    item_key: '1572:4' } ]

Click Library:

[ { title: 'Search',
    subtitle: null,
    image_key: null,
    item_key: '1573:0',
    input_prompt: { prompt: 'Search', action: 'Go' } },
  { title: 'Artists',
    subtitle: null,
    image_key: null,
    item_key: '1573:1' },
  { title: 'Albums',
    subtitle: null,
    image_key: null,
    item_key: '1573:2' },
  { title: 'Tracks',
    subtitle: null,
    image_key: null,
    item_key: '1573:3' },
  { title: 'Composers',
    subtitle: null,
    image_key: null,
    item_key: '1573:4' } ]

Click Home:

[ { title: 'Library',
    subtitle: null,
    image_key: null,
    item_key: '1574:0' },
  { title: 'Playlists',
    subtitle: null,
    image_key: null,
    item_key: '1574:1' },
  { title: 'Internet Radio',
    subtitle: null,
    image_key: null,
    item_key: '1574:2' },
  { title: 'Genres',
    subtitle: null,
    image_key: null,
    item_key: '1574:3' },
  { title: 'Settings',
    subtitle: null,
    image_key: null,
    item_key: '1574:4' } ]

Notice that the “item_key” field increments with every click. Unfortunately that is what is returned by the API. This behavior is mentioned in these threads as well (Browse API questions and Questions on Browse APIs). And that means that any entry that is cached will have an old/incorrect “item_key” - and navigation fails with an incorrect “item_key”. So caching is not useful.

Unfortunately this also means that if you start browsing on one device, move to another device, then return to the original device, the original device will not browse until you hit the “Refresh” icon to get the current “item_key” (which incidentally is why it is there! :slight_smile:)

The ever changing “item_key” is also one of the reasons why the web app uses HTTP POST to retrieve data from the Node server. HTTP POST requests are never cached and cannot be bookmarked, whereas HTTP GET requests can be.

Another reason is the fact that HTTP POST requests have an unlimited body size, whereas HTTP GET requests are limited to a URL length of 2,048 characters - which may be an issue if components of this web app is embedded in another web app. And since one of the features of this release is support for being embedded in other web pages, URL length could become an issue.

As for this one, that is being returned by the Roon API. That exact message is produced on line 162 of node_modules/node-roon-api/moo.js.

This is likely a connectivity issue between the Node application and the Roon Server itself. It is quite weird to see the number jump from “COMPLETE 2589” to “CONTINUE 2”. Those numbers are typically sequential, so I would have expected “2590” instead of “2”.

Between the Node application and the Roon Server, do you have any network connectivity issues such as Wifi dropping out? Laptop going to sleep? Network Manager acting weird?

Weird - I noticed that too after doing some JSHint related code clean up. I think it has to do with the settings or the state variables being reset because it is actually the theme being reset. Using the color theme will reset to the dark theme (the default) as well…

Lemme do some investigation on that. I just opened an internal bug for this.

1 Like

possibly, the whole setup is wifi … roon server to endpoint, laptop to roon server. I’ll keep an eye on it.

A few more suggestions:

  • It’d be nice to be able to see the bit depth and bitrate of the track that’s being played.
  • Could search be made universally accessible?
  • Recently added albums would be a welcome addition too
  • As would genre information in now playing
1 Like

I know I’m asking a lot, but @Mike_Plugge I suspect you knew it inevitable when you embarked on this project…

Could artist names, album names, track names and Genres be made clickable throughout e.g. clicking on the artist name in now playing takes you to their discography?

All great suggestions and oddly - all things I also wanted. But the API does not provide bit depth or new additions.

Clickable genres, artist, album, and tracks? Tough because of the previously mentioned “item_key” issue.

That and the fact that the “library” and the “now playing” screen are actually 2 completely separate HTML files that do not interact with each other. They are iframes in a 3rd html page used for layout. This was done to support embedding the widgets in other web pages but also to keep the code cleaner.

Universal search location is doable but would require that element to constantly be updated with the current “item_key”. Possibly next version.

I also wanted to do play queue management. But that is not exposed in the API either.

1 Like

Just wanted to say thanks for your efforts. It’s great having the ability to control Roon directly from a browser (and more so in my case from a laptop running Linux). I finally don’t have to go looking for a tablet or phone just to select and play an album.

2 Likes

I second @evand.
Thank’s a lot @Mike_Plugge , great work. I’m beginning to use your extension on a RPI 3 with official 7" Touchscreen display + case and it’s a perfect “Now Playing” (i also greatly appreciate the new “Library” capabilities).

For test purpose i have setup the touchscreen on my RPI USB audio endpoint (<100 euros). It works but it’s not a good deal from Sound Quality point of view because noise and interferences, probably pollutions on USB from the display hardware ?

So my plan is to use a speciific audio endpoint with no display (MicroRendu, SOtM Sms-200, or DIY Allo USBridge) and let my RPI with display touchscreen out of the Roon audio path: Only to run Chrome browser to display Web Controller.

Interested in feedbacks from other users using RPI + Touchscreen, especially on:

  • Same audio interferences with touch screen ?
  • How to autoload chrome browser at startup ?
  • How to adjust display backlight ?
    I’m running DietPi as OS and i’m not a LINUX nerd.

Again congrats @Mike_Plugge (and the Roon team for the API) !

2 Likes

I have a dedicated Raspberry Pi running to my stereo - one without a display. It has a HifiBerry Digi+ which is plugged into a NAD-D3020 via digital coax. I have a separate Raspberry Pi for the touch screen because it is in a different location. There may be some audio interference from the touch screen, but I am definitely not an expert there. My advice to you would be to try it and if you like it, stick with it. But you will most likely get a better audio experience out of a purpose built, dedicated music player.

One thing I would say though. At this time, I would not recommend a USB DAC on a Raspberry Pi. You will be better off with a dedicated RPi device such as a HifiBerry or an IQaudIO device. All of the Raspberry Pis have known issues with USB DACs that can cause popping and cracking in the sound output. There are some work around techniques with specific configurations, but it is still not as good as a dedicated RPi device.

As for getting things working on Diet Pi, I am not sure how much help I can be. I am very much a Linux geek and I exclusively run Arch Linux for Arm on all of my Raspberry Pis. As a Linux geek, I use the command line to adjust the display backlight. The brightness is adjusted with entries under the “/sys” pseudo file system.
Google “/sys/class/backlight/rpi_backlight/brightness” for more information

I describe how I launch Chrome/Chromium at start up in this link. But it is very specific to Arch Linux and the comment makes assumptions about Linux skill level. I provide this information as-is and cannot provide support for it.

@Tech_Whisky_Lab, @evand and @volpone - thanks for the kind words about the extension.

This project started it’s life as something for me to use myself and I only released it publicly on a whim. I have been very surprised and quite pleased that anyone else is actually using it. I am not a programmer by trade - I am a Linux sysadmin with extensive network engineering experience - so my code is probably garbage.

But I am pleased that others are using it and are happy with it!

2 Likes

I agree Mike.
Even before to test RPI touchscreen my projected setup was to use headless dedicated audio endpoint (without any display) to optimize SQ.

My tests with RPI touchscreen are a confirmation this is really required to avoid terrible audio interferences. Not a problem to get an independent dedicated web controller display on RPI out of the audio path.

About RPI USB SQ i’m currently using RPI3 + iFiPower PSU to feed T+A DAC8DSD, via USB, with upsampled audio up to PCM 384K and DSD128 (DoP): SQ is quite OK (no pops, no noise) either with RoonBrige or with NAA (HQPlayer client). Only issue is lack of native DSD support due to Amanero / ALSA drivers limitations. However, for even better USB I will try ALLO USBRIDGE as soon reviewed. I hope drivers will be fixed for native DSD.

Thank you for your valuable links and informations. I will ask on DietPi support forum too.

Hi Mike,
A little off web controller topic but about RPI + Touchscreen and audio interferences.

Seems it could be an “under voltage” issue. I’ve got a yellow lightning bolt symbol on the screen.
See discussion: Powering Raspberry Pi 3 & Touchscreen

That would not surprise me - especially on a Raspberry Pi 3. The RPi3 has higher power requirements than the older ones and the touchscreen pulls even more. I use a power supply rated at 5v, 2.5amp for my RPi3 with touchscreen. But most of the older power supplies are only 2.1amp.

@Tech_Whisky_Lab - I think I have this resolved and have pushed the update to the dev branch.

Can you check it out please?

1 Like