Help needed with a Pandora Plugin

I’ve been working on a Pandora plugin for the last week or so. I am using the Pianode npm module available on GitHub. That module is not actively being developed but there is a good amount of code to work with. Pianode is a Node front end to pianobar, which is a command-line Pandora client running in Linux. Thus, the playback is totally external to Volumio and the information comes in the form of events (onStationChange, onSongChange, etc). I believe it is possible to get the song time and length from the debug output but I have not gotten that far.

My plugin is not perfect at the moment. I can tune in stations and the information comes up on toasts (station name, song title). I have not figured out how to get this information into the Volumio GUI. Also, I have to uninstall the plugin to make it stop, which is not ideal. I’m sure these things can be corrected but I haven’t figured them out yet.

I can browse the stations in the Volumio GUI and change them. I have managed to queue up songs that were previously played, but they are not perfect yet. The play/pause button stays on Play so far and I can’t pause the playback because it’s calling Play every time.

I have to hard code the login and password into a settings.js file. I instantiate the object in onStart and start the process. I’m not very good with promises so I’m probably doing things wrong but it loads up okay.

It works well if you leave it alone. Pianobar is quite stable.

Will it be possible to put in time information for the tracks if I had the song lengths and current time positions?

Basically, all the Pianobar information is read from the console and parsed by Pianode. Pianobar sends keys via a socket connection. How can I update the GUI since pianobar is external to the Volumio program?

Thanks for any help. I don’t have the code posted yet – I have to remove a few things like my credentials and some backup files – but I will do that as soon as possible and let you know.

You need to extract the info from pianobar and then update the statemachine using the api we provide.
Basically you can take a look on how its done via the spotify plugin here:
github.com/volumio/volumio-plug … y/index.js

Let me know if you need more help

Thanks for your quick reply!

I can retrieve the song duration (in seconds)/artist/title/rating/album and artwork (URI) when the song is first played. I may be able to get the time remaining from stdout of pianobar if I can hack together a regular expression to extract it. I don’t see a call for updating the artwork in your Spotify plugin (at least not in the state machine part of it). Is this done there or somewhere else?

Pandora works (for me) by loading a list of stations, and then one is selected. Right from there a song is played and it can be skipped, loved or hated/banned. Then it just plays until it is stopped or paused, or another station is selected. Of course, one can add a new station but I’ll cross that bridge when I come to it. I just say this to outline a basic flow of events in case you are not familiar. I may have missed one or two but that is the essence. So a simple menu of depth=1 works just fine. The menu with stations works in that I can select stations. At this point, the station name and song information comes across as toasts which is not ideal.

I don’t know how to stop the Pianode object or process as I am not very familiar with Node JS. Do I have to add a method to the object and if so, what sort of quit command could I use? I know that is very basic but I’m just not sure what to do there.

When the plugin is enabled, it immediately instantiates a Pianode object. I can load it “paused” but it needs to have the login information. I have been able to successfully add the credentials and they are saved in the proper place, but when the plugin is first run, those values are blank and the Pianode object will fail. Again, I think the solution to this will be simple but I’m a little stuck. Is there a way to change the current web page to the configuration page if the credentials are empty or invalid? That would solve it, I think. I’d like to move the credentials out of my settings.js file! :slight_smile:

Would it make sense to call the parseState() function on an “onSong” event? Would I have to call parseState() every second or just at the start of each song (and probably when the song is paused I imagine)?

This is a big project for me as I’m usually a hack and slash sort of programmer, just writing short scripts and such. I think I can do this but it will probably take me more time than for a seasoned Node JS developer, or for a seasoned developer in general. :slight_smile:

Thanks for helping me out!

I have gotten slightly further along with this plugin. I am sure I am doing something wrong; maybe this will make my situation more clear and someone can help me out here?

Music is playing but the main Volumio display with the song/track information is blank. I have the stop() method in onStop, and it stops when I switch the plugin to “off” in “Installed Plugins”, but the interface restarts and the plugin loads back up afterwards.

I still have the login information hard-coded into a settings.js file. I have a menu item loaded in Volumio and the values save, but I’m not sure what to do about the initial loading, since the object needs the login info to load itself. I’ll keep at this but I’m more concerned about displaying the song metadata at the moment.

As far as I can tell, the stream should be a 64kbps AAC file if that is needed here. I can try to verify this. I only have the free version of Pandora.

Here is what I have in parseState:

[code]//Parse state
ControllerPandora.prototype.parseState = function(state) {
var self = this;
self.commandRouter.pushConsoleMessage(’[’ + Date.now() + '] ’ + ‘ControllerPandora::parseState’);

var sStatus;

var myPromise = new Promise(function(resolve,reject) {
    var objStatus = self.pandora.getStatus();
    if (objStatus.status === 'playing') {
        sStatus = 'play';
    } else if (objStatus.status === 'paused') {
        sStatus = 'pause';
    } else if (objStatus.status === 'not running' || objStatus.status === 'undefined') {
        sStatus = 'stop';
    }

    if (state && sStatus) {
        var parsedState = {
            status: sStatus,
            service: self.serviceName,
            type: 'pandora-station', // is this needed?
            position: state.timePlayed,
            seek: state.timePlayed * 1000,
            duration: Number(state.songDuration),
            //samplerate: self.samplerate, // is this needed? 
            //bitdepth: null, // is this needed?
            channels: 2,
            artist: state.artist,
            title: state.title,
            album: state.album,
            albumart: state.art
        };
        resolve(parsedState);
    }
    else {
        reject('Bad state/status information passed into parseState');
    }
});

return myPromise;

//Use this method to parse the state and eventually send it with the following function

};[/code]

And below is the log from journalctl:

Jun 08 11:20:17 volumio volumio[877]: [20B blob data] Jun 08 11:20:17 volumio volumio[877]: info: [1528456817464] ControllerPandora::parseState Jun 08 11:20:17 volumio volumio[877]: Time Jun 08 11:20:17 volumio volumio[877]: info: [1528456817466] ControllerPandora::pushState Jun 08 11:20:17 volumio volumio[877]: info: [Pandora] Pushing state Jun 08 11:20:17 volumio volumio[877]: info: CoreCommandRouter::servicePushState Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::syncState Jun 08 11:20:17 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:17 volumio volumio[877]: info: STATE SERVICE {"status":"play","type":"pandora-station","position":36,"seek":36000,"duration":577,"channels":2,"artist":"Miles Davis Quintet","title":"Footprints (Antwerp, Belgium 10/28/67)","album":"Live In Europe 1967: The Bootleg Series Vol. 1","albumart":"http://cont-2.p-cdn.us/images/public/rovi/albumart/5/2/3/5/886979405325_500W_500H.jpg"} Jun 08 11:20:17 volumio volumio[877]: info: CURRENT POSITION 0 Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::syncState stateService play Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::syncState currentStatus play Jun 08 11:20:17 volumio volumio[877]: info: Received an update from plaugin. extracting info from payload Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::pushState Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::getState Jun 08 11:20:17 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:17 volumio volumio[877]: info: CoreCommandRouter::volumioPushState Jun 08 11:20:17 volumio volumio[877]: info: CoreCommandRouter::executeOnPlugin: volumiodiscovery , saveDeviceInfo Jun 08 11:20:17 volumio volumio[877]: info: interfaceApi::pushState Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::pushState Jun 08 11:20:17 volumio volumio[877]: info: CoreStateMachine::getState Jun 08 11:20:17 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:17 volumio volumio[877]: info: CoreCommandRouter::volumioPushState Jun 08 11:20:17 volumio volumio[877]: info: CoreCommandRouter::executeOnPlugin: volumiodiscovery , saveDeviceInfo Jun 08 11:20:17 volumio volumio[877]: info: interfaceApi::pushState Jun 08 11:20:17 volumio volumio[877]: info: Pushing Favourites {"service":"mpd","uri":"","favourite":false} Jun 08 11:20:17 volumio volumio[877]: info: Pushing Favourites {"service":"mpd","uri":"","favourite":false} Jun 08 11:20:18 volumio volumio[877]: [20B blob data] Jun 08 11:20:18 volumio volumio[877]: info: [1528456818465] ControllerPandora::parseState Jun 08 11:20:18 volumio volumio[877]: Time Jun 08 11:20:18 volumio volumio[877]: info: [1528456818467] ControllerPandora::pushState Jun 08 11:20:18 volumio volumio[877]: info: [Pandora] Pushing state Jun 08 11:20:18 volumio volumio[877]: info: CoreCommandRouter::servicePushState Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::syncState Jun 08 11:20:18 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:18 volumio volumio[877]: info: STATE SERVICE {"status":"play","type":"pandora-station","position":37,"seek":37000,"duration":577,"channels":2,"artist":"Miles Davis Quintet","title":"Footprints (Antwerp, Belgium 10/28/67)","album":"Live In Europe 1967: The Bootleg Series Vol. 1","albumart":"http://cont-2.p-cdn.us/images/public/rovi/albumart/5/2/3/5/886979405325_500W_500H.jpg"} Jun 08 11:20:18 volumio volumio[877]: info: CURRENT POSITION 0 Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::syncState stateService play Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::syncState currentStatus play Jun 08 11:20:18 volumio volumio[877]: info: Received an update from plaugin. extracting info from payload Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::pushState Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::getState Jun 08 11:20:18 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:18 volumio volumio[877]: info: CoreCommandRouter::volumioPushState Jun 08 11:20:18 volumio volumio[877]: info: CoreCommandRouter::executeOnPlugin: volumiodiscovery , saveDeviceInfo Jun 08 11:20:18 volumio volumio[877]: info: interfaceApi::pushState Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::pushState Jun 08 11:20:18 volumio volumio[877]: info: CoreStateMachine::getState Jun 08 11:20:18 volumio volumio[877]: info: CorePlayQueue::getTrack 0 Jun 08 11:20:18 volumio volumio[877]: info: CoreCommandRouter::volumioPushState Jun 08 11:20:18 volumio volumio[877]: info: CoreCommandRouter::executeOnPlugin: volumiodiscovery , saveDeviceInfo Jun 08 11:20:18 volumio volumio[877]: info: interfaceApi::pushState Jun 08 11:20:18 volumio volumio[877]: info: Pushing Favourites {"service":"mpd","uri":"","favourite":false} Jun 08 11:20:18 volumio volumio[877]: info: Pushing Favourites {"service":"mpd","uri":"","favourite":false}

Again, thanks for any and all help. I’m only trying to give back in a small way to the open source community for all the things I use every day.

I took a small vacation to Maine and took a break from this plugin. I caught a head cold and threw away a tree’s worth of facial tissues in this week’s garbage. I think I’m mostly well now and I have resumed work on this plugin.

I have it on my GitHub page. You can clone my fork of the plugins tree here: https://github.com/truckershitch/volumio-plugins.git It is under music_service/pandora

This is very much a work in progress. I’m going to fix the initial station. The Pianode library thinks you can enter a Q there but it is invalid. I thought about starting with station 1, whatever that is. I will implement a save to settings for the current station, but I’m pretty tired right now and I need a beer. I’ll look into that in the next few days. That’s very simple.

Problems:

  1. Logging in is not fully working. You can either hard-code your credentials into settings.js and uncomment the relevant parts in the onStart portion of index.js (commenting out the other relevant lines) or try your luck. It will save your entries for email and password but in all probability it will crash at first. I have to:
    [login via ssh]
    $ stop
    $ rm -rf /data/plugins/music_service/pandora
    $ start
    [make sure I’m in the pandora cloned directory]
    $ volumio plugin install
    After that, the saved values for email and password will be applied without having to comment/uncomment anything. Note that uninstalling the plugin with the GUI will clear your credentials!

  2. Pause works. Resume/Play does not. I don’t know how to add my own function for play here. While paused, if you browse for the Pandora icon, you can change stations and it will resume playing. Awkward, I know.

  3. Seek is not (yet) implemented.

  4. Exit/Quit/Restart is pretty much not working. Things break. See step one for recovery. Your credentials will be saved as long as you don’t uninstall the plugin with the GUI. I need to fix this.

  5. This is VERY ALPHA. If you want to just pick a station or two and play them, it will run for hours. The metadata shows, the time remaining/total/artist/album/song/etc. So it sorta works but it’s not safe for your parents. Your mileage may vary, as will the skill of your respective parents.

  6. Please be kind. I’m not a professional programmer, and the work I used to do is a lot different and was in a different language. This is a learning experience for me. I want to give back because I have taken so much from the computing community and hopefully this will help repay my debt to society. Git is also a challenge for me but I’ve been using this guide: http://rogerdudler.github.io/git-guide/

I hope someone can use this and who knows, it may actually work better down the road…

I’m running into an issue when Volumio stops and then starts back up. The Pandora plugin attempts to load and then this error occurs:

[code]info: PLUGIN START: pandora
info: [Pandora] Instantiated Pianode
info: [Pandora] Started Pianode
info: CoreCommandRouter::volumioGetState
Pianode closing. Killing pianobar.
/volumio/app/index.js:122
return this.stateMachine.getState();
^

TypeError: Cannot read property ‘getState’ of undefined
at CoreCommandRouter.volumioGetState (/volumio/app/index.js:122:26)
at ControllerPandora.iscurrService (/data/plugins/music_service/pandora/index.js:602:45)
at ControllerPandora.onStart (/data/plugins/music_service/pandora/index.js:108:15)
at PluginManager.startPlugin (/volumio/app/pluginmanager.js:256:27)
at HashMap. (/volumio/app/pluginmanager.js:332:29)
at HashMap.forEach (/volumio/node_modules/hashmap/hashmap.js:157:10)
at HashMap.proto.(anonymous function) [as forEach] (/volumio/node_modules/hashmap/hashmap.js:169:7)
at PluginManager.startPlugins (/volumio/app/pluginmanager.js:331:15)
at new CoreCommandRouter (/volumio/app/index.js:63:24)
at Object. (/volumio/index.js:41:21)
at Module._compile (module.js:570:32)
at Object.Module._extensions…js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Timeout.Module.runMain [as _onTimeout] (module.js:604:10)
[/code]
I call this with the ControllerVolspotconnect.prototype.iscurrService() function which I copied over into my index.js file after seeing this issue. It looks like the stateMachine is undefined when Volumio starts up after my plugin has been loaded and I have stopped the service (systemctl stop volumio.service). It could be that there is a problem when it shuts down.

I tried to unset the volatile mode from the onStop function but perhaps this is not occurring?

Does anyone have a suggestion? I’d really appreciate it.

Okay, wrapping the “set volatile” code in a setTimeout for five seconds took care of it, but maybe there is a better way? Could I test to see if the object is defined and then execute the code somehow? My test machine is a Pi3 B+ – wouldn’t the timeout have to be increased for a slower machine, and therefore the “fix” is problematic? Of course, I could add the timeout to the start of the instantiated object instead of afterward.

This asynchronous methodology is not the kind of thinking I’m used to. If someone has a better idea, I’d love to hear it.

It really seems like I’m talking to myself here, but I have another announcement.

I did a fresh pull from my git repo this morning to make sure that someone could do a ‘npm install’, then a ‘volumio plugin install’ and be off to the races. I found out that I hadn’t taken care of the build dependencies properly. I’ve been running with the .deb packages installed all this time and hadn’t tried a fresh start from scratch in a while.

So if anyone has tried to get this to work, try again, because I’m pretty sure you would have had to poke around and look for dependencies until now.

Some changes:

Plugin now cleanly exits if disabled. This was due to a process.exit() call in the main dependent npm library (pianode).
Metadata works pretty damn well now. The refresh rate with the state changes on my Pi 3B+ is best, but this also works pretty well with the Pi 2.
As I mentioned, some installation issues were fixed.

Problems:

With the volatile state enabled (I think this is the only way I can go since all audio work is done outside of Volumio/mpd) I cannot get Play or Resume to work. They seem to want to call mpd. Since I haven’t had any luck lately with clearAddPlayTrack() (and I’m not sure this is relevant), I can’t restart. Pause works. For now, I disabled the UI controls. It’s a kind of fix.
I am currently having issues getting this working on Pi 1B. I’m not sure if this is a problem or not. Volumio seems very, very slow on a Pi1 B. But I suppose there are some who want to run a Pi Zero with a hat, and this probably needs to be fixed. I suspect there are problems with some setTimeout() calls, and these may need to be changed to something more reliable.

I’d really love someone to get in there and break something. I have been doing a good job of it myself so far, but more eyes would be great.

Thanks.

Hey truckershitch,

You are kind of talking to yourself. :laughing:

I’m finishing up my own plugin for http://phish.in/, but as soon as I’m done I’ll take a look at this and help to test. I’ll be on vacation for a week or so, and when I return I’ll load this up and see if there’s anything I can to to help.

Noah

Thanks Noah! I’ll take a look at your plugin, too. I have Junta on CD somewhere but I’ve never seen Phish live. I’ll check it out though.

Have a good vacation. I think my vacation helped me gain perspective on this thing (and everything else) by forcing me to stop looking at things for a while. When I got back, I felt much better.

Take care!

Some changes:

  • Replaced problematic setTimeout logic with an emitted “loggedIn” event. This fixed the errors with the Pi1B (and almost certainly with the Pi Zero which I do not own; so far untested. It’s the same chipset as the Pi1B as I understand it). So that’s the RPi1B, RPi2, and RPi3B+.
  • Some small fixes were made to cover potential user configuration problems.
  • A small Perl one-liner was added to install.sh to create a ~/.libao configuration file. This fixes the issue of having an add-on sound card. Pianobar will use the device configured in Volumio. Before installation of the plugin, sound must be working in Volumio, otherwise the plugin will have to be installed again and the ~/.libao file will be overwritten.
  • Small sed script was added to install.sh to correct path names in the Pianobar config file generated by the Pianode library during installation.
  • Station was not saved if the plugin was disabled and re-enabled – it was going back to station 0. This was due to a Boolean flag that was only initializing at the start of the process. I’m not sure why variable states are being saved when the plugin is disabled. At any rate, I just dealt with the Boolean flags (there was one other issue with the “loggedIn” event) and the station is now saved.
    From what I can tell, Javascript disposes of objects automatically with its garbage collection and it’s not typical to do something analogous to object.dispose() or set object = Nothing. So I suppose this Pianode object is just floating around out there and it gets reused. I’m happy to trash it and reload another one if someone has a better way, but maybe this is harmless.
  • Minor bug fixes.

I did try to add on a Create Station function, but found that Pianobar was returning a list of possible stations to choose from. I don’t know if there is a way to do a proper user dialog, so I went back to the current state of affairs and saved the code for a possible later date.

I feel this is ready to test. I haven’t run into a bug in the last couple of days, so that’s a good thing. If anyone does, let me know and I’ll see what can be done.

I was also trying to do a proper user dialogue but the same bug occurred i am already using the Pandora by unlocking and trying then this kind of things also happening with its plugins. BTW let me know when you have found the fix

Here’s an update for anyone who’s listening.

Changes:

The biggest change is to the handling of the /home/volumio/.libao file creation. I put an option to refresh this file in the plugin settings page. Now, if you change your soundcard settings, you can refresh the .libao file from the Volumio sound configuration. This is small stuff but will save the typing.

I changed the .libao creation script from Perl to Node.js in the spirit of staying in the same language. What used to be a one liner is now several lines of code, but it does return a promise and has error checking (which is probably unnecessary but there it is).

The code is up on Github. I wrote a readme.md with basic instructions. I did not mention how to enable SSH but I think that people can figure that one out? Others have it on their pages. It’s not hard to install it, you have to type like 3 or 4 lines of code and wait.

Questions:

I think that Liking/Disliking songs is not going to happen unless I can somehow better interact with the user. Maybe with a modal dialog, but how will the user pull it up? Am I missing something? Likewise with creating stations. I tried to work with the search bar for that one and I’ve hit a wall with events.

I’m really trying to get the buttons in Volumio to work (pause/play, next). It sorta works in my development version but pause never unpauses, and I can get it working when it’s in the debugger, but not when run normally. I’m sure it’s some kind of timing issue but I’ve tried several things and nothing is working. What seems to be happening is that Volumio thinks Pandora has completely stopped as it’s not calling my Pause, it’s calling Volumio Pause. If anyone has any idea what to do about it, please let me know. Michelangelo is apparently on vacation until near the end of August so I’m hoping someone would take a look at it?

Is there a way to pull up the Browse -> Plugin -> list that handleBrowseUri() works with? If I can produce a menu then I can probably get somewhere with creating stations from the Search dialog.

So this is pretty stable at this point. You turn it on, and disable the plugin to turn it off. I do hope to enable the controls. So go for it, crank up the tunes. It works fine as it is and down the road it will get better.

Thanks for tuning in.

Hi truckershitch,

I finally got a chance to load this a mess around with it. First off – great work. There’s a lot you’ve accomplished here – playback sounds great and the tracks/timer all displayed correctly. You’ve gotten a lot done!

So, I think a lot of problems you’re having with playback controls have to do with the particular way pianode/pianobar works. It’s not really a deamon, in that it cannot really run in the background without playing. Both pianobar and Pithos (the GTK/Python gui client) work this way – as soon as you start the application (and choose a station) playback begins. You can pause that playback, but can only stop it if you close/kill the application.

If you want to continue to use pianode, I think you’re going to have to find a way to only start pianobar (via the pianode command: “self.pandora.start()”) when a station is selected, not when the plugin is started. But now your kindof stuck in a catch 22 with pianobar – you can only query station/playlist information from pianobar when it is started but starting it via pianode starts playback, and stopping playback kills pianobar so that you can not longer query station/playlist information.

Maybe a better technique would be to ONLY start pianobar via pianode when the station/playlist is selected from the Volumio menu. That would mean you will need to query station/playlist information outside of pianode. I think that can be done with the same unofficial API that pianode uses. It looks like that’s the way pianobar interacts with Pandora. (Gosh, you could probably completely write a Volumio plugin from scratch only using the API and MPD, but that seems like a lot of work when pianobar/pianode already does playback and control).

So, on plugin start you’d prep everything but never actually start pianobar. When a Volumio user browses to the Pandora app, it uses the API to get the station/playlists. Then choosing a station will start pianobar via pianode. I imagine then you could bind the Volumio playback controls to pause/play and skip pianobar via the pianode commands. Probably could even get “ban” and “love” to work if there’s a way to add Volumio UI elements via a plugin.

Anyway, does this help? I can help if you’d like. I’m the same sort of programmer as you (a hobby hacker), but maybe another set of eyes could be a big help.

Hi lostmyshape! Thanks for taking a look at my plugin!

I think you have good ideas. Like you said, pianobar logs in and starts playing, and won’t yield a station list until it starts up. It also sucks that I’m having to wait for events to fire off based on stdout from pianobar.

The initial reason I started with Pianode is that those devs already had (mostly working) code in Javascript and I thought I could ‘duct tape’ it together. I think I’ve managed to do that, but I’m hitting some difficulties, some of which you’ve pointed out.

Last night and this morning, I got Play/Pause working for the first time, but now it seems that I’m having an issue if there is anything in the Queue/Playlist (the third section of the main UI). I had a TuneIn station in there. Pause (pause) worked but it was killing the ‘Pause (resume)’. Clearing the queue fixed it.

I have been experimenting with pushing items into the queue manually, possibly after a call to self.pause(), but I haven’t had success yet. The items show up but things aren’t working quite right. Sometimes the output pauses and restarts. The queue is not cleared and I think that has something to do with clearAddPlayTrack(). Sometimes the Pianode object is started all over again. I’m not sure of the proper behavior here. I think it should be “ignore the queue entirely and just resume playback”. Putting the station into a Queue seems extraneous to me when the stations are all listed in a menu anyway.

I think Volumio is looking at the queue and deciding that that current service is mpd. So maybe I can fix that with some “Mortal Kombat” combo of self.commandRouter.x.y statements, but I’m not sure what they would be. I’ll keep banging away. I hope I don’t burn a hole through my microSD card with all these removals and installations of my plugin!

I’ll see if I can get a handle on grabbing the stations from elsewhere. It might be possible to load up that other Javascript Panodora.js library that was linked from your article and use it to query the stations as you suggest. That would seriously help the cause!

I’m going to take some time today and look at the information you posted.

I would certainly appreciate your help. Thanks for offering!

Yeah… how about this node module for the unofficial API: npmjs.com/package/anesidora. You might be able to use that in conjunction with pianode… or just use it with MPD. Technically, it seems you can get URLs to the mp3s from the API. I have experience sending web URLs to MPD and controlling MPD in Volumio from my plugin. I think it is possible to have something that functions kindof like the 80s80s plugin, but with Pandora stations.

I just tested Anesidora here: https://npm.runkit.com/anesidora. It works and is really easy. It would be trivial to use Anesidora and the user.getStationList method to populate the Volumio Browse menu. Then you could probably use pianode/pianobar to play the same way you’re currently doing it.

…or maybe the station.getPlaylist method could be used to add tracks to the MPD playlist. The method returns 5 tracks at a time, so there would need to be a way to get 5 more tracks at the end of the playlist. But I think that could be done.

I’m here to help if you need it!

Hey that’s great! I just put in that example snippet into a fresh .js file and did it locally (I was a little nervous about putting my username & password on a public form, but I probably shouldn’t have been). Anyway, it spit out a ton of station information! I can load that up and get the ball rolling with a better work flow.

Thanks a lot! I’d like to try to redo this as the weeks go by and feed into mpd as you suggest. I’ll have to wrap my head around the methodology and the flow. I’m getting better with callbacks, promises, events, etc. but it’s still far from the code I learned in college.

I was totally unaware that such an API existed until you showed it to me. I started this plugin with an O’Reilly book on Javascript and have been learning ever since. Now that I have a better idea of how asynchronous programming works, I might be able to handle something as powerful as this interface.

Pianode is all right, but the socket stuff, grepping for information in a constant stream of text from pianobar’s stdout, it’s very clunky. It works but it’s far from ideal.

You’re right about incorporating “like” and “dislike” functionality. It should be doable but I don’t know how to add buttons to Volumio. Pianode has functions for it.

I think you can push an array of tracks to mpd if I’m not mistaken. I think maybe Marcus did something like that with his plugin? I’m not sure, but I think I’ve seen an array being pushed.

I’m going to leave this until tomorrow as I’m feeling a little tired after dinner. I’ll definitely reach out when I hit a wall. Thanks again!

Glad I could help! Yeah, I think the Radio Paradise plugin works by loading a few tracks at a time. I took a look at that code, and I think Marco had to run a separate timer to get track time, but otherwise the code doesn’t look that complicated. It’s probably a good template.

I’d love to figure out how to add to the UI in a plugin. I’m not sure that it is possible but I’ll poke around and see what I can find.

Good luck coding and let me know if you want any more help!

Hi lostmyshape!

I’ve been working on this with the API you suggested. Everything works except pushing the state. The basic track information is shown in Volumio minus the artist, title, album, and artwork. Several tracks (four at this point) are fetched from the server and my plugin feeds them into mpd one at a time. The loop goes and goes.

I feel pretty good about this because I had to figure out how to promisify the basic callback Unofficial Pandora API functions. Granted, when I look at it now, it all seems pretty easy, but I was stuck on that for about two days. I think I now have a much stronger feeling for how Promises work. If I try to tackle another project, I’ll have more skills for next time. The first time I started to wrap my head around asynchronous programming, I was very confused. Finally, I have a better grasp of how things work and I owe it all to this project.

But back to the problem: pushing the state information. The first thing I tried was Marco’s way, since I had borrowed a good bit from his code. Volumio crashes on this line. The position key is missing when I run though the code in the debugger. playQueue.arrayQueue is empty, zero items. At that point in the program, there is one track loaded into mpd. I do have the state information for it and I have tried to just stick it in with

return self.mpdPlugin.getState() .then(function (state) { self.commandRouter.servicePushState(state, self.serviceName); });
That also has no effect. The values I mentioned earlier(artwork, title, artist, etc.) are null when returned from the mpdPlugin object and I updated the state object with my values to no effect.

I put my code up on my Github page here. It’s in a new branch called ‘develop’. I think you just go:

git clone https://github.com/truckershitch/volumio-plugins git checkout develop
but I’m not 100% on that as this is my first branch. Woohoo! Yeah. Well I was excited that it worked. That makes one of us. Git is a little terrifying. I triple-check when I’m making changes.

As always, all help is appreciated. This is going to work better than the other version once the track information works. It is very likely that I am making a boneheaded error.

Thanks for tuning in. I’m going to grab a beer.