PDA

View Full Version : TinySC question..



gharris999
2010-01-14, 20:40
This is following up on Triode's question of a couple of weeks ago:

Given a list of SBSs returned by Slim::Networking:: Discovery::Server::getServerList(), how would one determine whether a given *other* server was 'Tiny' or 'Fat'?

I suppose that one could try to fetch http://<server>:<port>/music/current/cover.jpg and that attempt would fail against a TinySC, but that seems horribly inefficient.

Is there any other way?

peterw
2010-01-14, 22:00
I suppose that one could try to fetch http://<server>:<port>/music/current/cover.jpg and that attempt would fail against a TinySC, but that seems horribly inefficient.

And ineffective if you don't have the username/password for the remote server. I've submitted a patch that would let you see the version of a server without first authenticating, but I don't know if there's much interest in it: https://bugs.slimdevices.com/show_bug.cgi?id=14835#c13. I think the general feeling at Logitech is that systems with security enabled are atypical, so requests like mine (https://bugs.slimdevices.com/show_bug.cgi?id=14789) naturally get prioritized lower.

gharris999
2010-01-15, 10:17
I'd really appreciate an answer from a SBS developer here. If the answer is 'No, there isn't any way we know of.'...please say so. That way I can file an enhancement request and get on with things.

What about some kind of CLI request? Would the response to any of the possible 'can' or 'prefs' requests differentiate a TinySC from a FatSC?

PeterW: your point is taken. Thanks. I hadn't thought of that.

andyg
2010-01-15, 10:22
The information you want to know is *NOT* whether a server is "Tiny" or "Fat", because that's not how it works. What exactly do you want to know about the server? We can probably add some additional field(s) to describe the desired capabilities of the server.

gharris999
2010-01-15, 19:56
Andy: you are all wise and all knowing. I humbly bow before thee. And, Omnipotent being that you are, of course you already know what it is that I *really* want to know, so I should hardly have to tell thee.

What I *thought* I wanted to know, before you so graciously corrected my errant ways, was this:

I'd like to provide a feature in the SrvrPowerCtrl plugin that will automatically switch players connected to a FatSC to an available TinySC when the plugin shuts down or suspends the FatSC machine. In order to do that, I mistakenly thought that I had to somehow identify another SBS as a 'TinySC'. But you apparently have a much more wonderful and magical solution to this problem because apparently I do *NOT* want to know whether a server is 'Tiny or "Fat".

I humbly await your instruction, all-knowing one.

peterw
2010-01-15, 21:03
I'd like to provide a feature in the SrvrPowerCtrl plugin that will automatically switch players connected to a FatSC to an available TinySC when the plugin shuts down or suspends the FatSC machine.

What if you found two TinySBS hosts? What if I had 1 i7 monster w/ 4tb of flac, 1 Atom netbook w/ 100 gb of mp3s, and 1 Touch running TinySBS because there's a thumb drive plugged in? Maybe I normally want flac from the i7 but would prefer to fall back to the Atom (has web ui, runs plugins). Wouldn't most users want to specify their own preference?

gharris999
2010-01-15, 22:40
Wouldn't most users want to specify their own preference?I'm thinking that most users would like to see the TinySC presented at the top of the list of alternatives. And that's what I'd like to provide: ranking, with the low energy user given top billing. You know: the kind of feature that folks appreciate in intuitive design. But it's looking increasingly like there isn't a way to do this. So yes, I'll present all the servers discovered on the network as available options and let the user sort it out.

peterw
2010-01-15, 23:11
I'm thinking that most users would like to see the TinySC presented at the top of the list of alternatives. And that's what I'd like to provide: ranking, with the low energy user given top billing. You know: the kind of feature that folks appreciate in intuitive design.

Sounds smart and good.


But it's looking increasingly like there isn't a way to do this. So yes, I'll present all the servers discovered on the network as available options and let the user sort it out.

Isn't doesn't mean won't be. Andy seems interested in including this. I'd like to suggest following the examples in RFC 2616 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.38) and having the Server response header include strings like

Squeezebox Server/7.4.2-TRUNK linux x86_64-linux-gnu-thread-multi
where the OS "product" would be simply Perl $^O and the architecture would simply be $Config{archname} -- would that be adequate for your needs? (My reading of RFC 2616 section 3.8 is that version numbers are optional in product tokens, so I'd skip them for the OS and Arch data here.)

erland
2010-01-15, 23:40
The information you want to know is *NOT* whether a server is "Tiny" or "Fat", because that's not how it works. What exactly do you want to know about the server? We can probably add some additional field(s) to describe the desired capabilities of the server.

Is there some command already today to get the capabilities of the server ?

Some applets are going to depend on 3rd party plugins installed on the server, to be able to handle this nicely it would be a good idea to be able to check if the server supports 3rd party plugins.

mherger
2010-01-16, 01:34
You might want to use the systeminfo query which will return the data found in Settings/Information, eg.

"OS: Mac OS X 10.6.2 - DE - utf8 ",
"Plattform: x86_64",
"Perl-Version: 5.10.0 - darwin-thread-multi-2level",
"Database Version: DBD::SQLite 1.29 (sqlite 3.6.22)",

Parse that data and build your own logic. Touch would return "SqueezeOS" in the OS field, "arm-linux-gnueabi" in the Platform etc.

peterw
2010-01-16, 06:30
Some applets are going to depend on 3rd party plugins installed on the server, to be able to handle this nicely it would be a good idea to be able to check if the server supports 3rd party plugins.
Gordon has a nice approach to seeing if a plugin is already installed -- SvrPowerControl has an informational CLI command.

gharris999
2010-01-16, 09:33
You might want to use the systeminfo query which will return the data found in Settings/Information, eg.

"OS: Mac OS X 10.6.2 - DE - utf8 ",
"Plattform: x86_64",
"Perl-Version: 5.10.0 - darwin-thread-multi-2level",
"Database Version: DBD::SQLite 1.29 (sqlite 3.6.22)",

Parse that data and build your own logic. Touch would return "SqueezeOS" in the OS field, "arm-linux-gnueabi" in the Platform etc.
That sounds very promising. Thank you.

gharris999
2010-01-16, 11:24
You might want to use the systeminfo query which will return the data found in Settings/Information, eg.

"OS: Mac OS X 10.6.2 - DE - utf8 ",
"Plattform: x86_64",
"Perl-Version: 5.10.0 - darwin-thread-multi-2level",
"Database Version: DBD::SQLite 1.29 (sqlite 3.6.22)",

Parse that data and build your own logic. Touch would return "SqueezeOS" in the OS field, "arm-linux-gnueabi" in the Platform etc.
Alright, I'm embarrassed to put my ignorance on parade like this. Again, my plugin needs to query the OTHER server, not the server the plugin is running on.

Since 'systeminfo' doesn't seem to be a valid CLI request, I assume that you must be talking about me making some sort of AJAX/JSON POST request to the other server's jsonrpc.js. Correct?

Forgive me, but my JSON fu is so poor as to be laughable. In fact, I don't even know the nomenclature enough to be able to express myself.

Attempting a quick prototype in javascript with IE:


function GetServerInfo () {
alert('GetServerInfo!');

var srvHttp = new ActiveXObject ('Msxml2.XMLHTTP');

srvHttp.onreadystatechange = function() {
alert('readyState == ' + srvHttp.readyState);
if (srvHttp.readyState==4) {
if (srvHttp.status) {
alert('status == ' + srvHttp.status);
alert(srvHttp.responseText);
// Inspect the JSON data as it is parsed..
var serverinfo = JSON.parse (srvHttp.responseText, function(key, value) {
var szPad = " ";
szPad = szPad.slice(0,20-key.length);
alert( "key: \'" + key + "\'" + szPad + "value: \'" + value +"\'" );
return value;
});
}
}
};

srvHttp.open ('POST', 'http://otherserverip:9000/jsonrpc.js', true, username, password);
srvHttp.setRequestHeader ('X-Requested-With', 'XMLHttpRequest');
srvHttp.setRequestHeader ('Content-type', 'application/x-www-form-urlencoded');

srvHttp.send ('{"id":1,"method":"slim.request","params":["",["systeminfo"]]}');
alert('GetServerInfo done!');

}


..returns no results.

I see how the perl function Slim::Menu::SystemInfo::infoServer() constructs the information for /HTML/EN/settings/server/status.html. I just don't understand how to fetch that information from another server.

May I please beg for another hint?

Obvious follow-up question: why doesn't the CLI 'serverstatus' expose all the information found on the webUI Settings/Information page?

mherger
2010-01-16, 14:13
> Since 'systeminfo' doesn't seem to be a valid CLI request, I assume

It is:

Slim::Control::Request::addDispatch(
[ 'systeminfo', 'items', '_index', '_quantity' ],
[ 0, 1, 1, \&cliQuery ]
);


> that you must be talking about me making some sort of AJAX/JSON POST
> request to the other server's jsonrpc.js. Correct?

CLI and JSON/RPC are interfaces. They both use the same command set. But yes, I'd suggest you use jsonrpc.js.

> Forgive me, but my JSON fu is so poor as to be laughable. In fact, I
> don't even know the nomenclature enough to be able to express myself.

json.org will help you get started. It's simple and worth the effort.

> Attempting a quick prototype in javascript with IE:

Don't go low-level. There are libraries for almost all languages. In most of them you build an object/arry/hash and let the library convert it into json for you. No need to create it yourself. I didn't read that script in detail, but look at firefox/firebug communicating with SBS.

> I see how the perl function Slim::Menu::SystemInfo::infoServer()
> constructs the information for /HTML/EN/settings/server/status.html. I
> just don't understand how to fetch that information from another
> server.

Here's what SP sends to SBS:

["systeminfo", "items", 0, 200, "menu:1", "useContextMenu:1"],

It's not too trivial in this case, as systeminfo will return a menu structure which you'd need to follow to the second level ("walk the menu"). I'll do some more testing when I find a little more time. I hope this already helps a bit.

--

Michael

pippin
2010-01-16, 14:59
json.org will help you get started. It's simple and worth the effort.

What it doesn't tell you is the application layer protocol, that is: how the command is wrapped into JSON. This is not at all obvious and you have to know it.
I am not 100% sure how it is (will have to look it up) but it's NOT just...


["systeminfo", "items", 0, 200, "menu:1", "useContextMenu:1"],

but instead something like


{"id":1,"method":"slim.request","params":[<player-id>,[command, param1, param2,...]]}

Would make a cool Wiki entry :). Maybe tomorrow when I'm done writing up that spec...

gharris999
2010-01-18, 20:00
What it doesn't tell you is the application layer protocol, that is: how the command is wrapped into JSON. This is not at all obvious and you have to know it.
I am not 100% sure how it is (will have to look it up) but it's NOT just...

but instead something like


{"id":1,"method":"slim.request","params":[<player-id>,[command, param1, param2,...]]}

Would make a cool Wiki entry :). Maybe tomorrow when I'm done writing up that spec...
Please let me know when you have the wiki entry underway!


> Since 'systeminfo' doesn't seem to be a valid CLI request, I assume

It is:

Slim::Control::Request::addDispatch(
[ 'systeminfo', 'items', '_index', '_quantity' ],
[ 0, 1, 1, \&cliQuery ]
);
My mistake. That said, 'systeminfo' doesn't appear on any of the CLI help pages.

Don't go low-level. There are libraries for almost all languages.
OK, what's already being used in SBS? I see that SBS's CPAN contains JSON::XS. Is that the correct module to use? Are there any existing SBS perl modules that are making calls to jsonrpc.js that I could crib from?

I.e., doing a grep -r "jsonrpc.js" /usr/share/squeezeboxserver/server/Slim

..yields hits in:

Control/Commands.pm
GUI/ControlPanel.pm
Networking/Discovery/Players.pm
Web/JSONRPC.pm
Utils/SQLiteHelper.pm

Where would you recommend that I start?


It's not too trivial in this case, as systeminfo will return a menu structure which you'd need to follow to the second level ("walk the menu"). I'll do some more testing when I find a little more time.
OK, correcting my little javascript semi-pseudo code with the correct parameters supplied by you gives me:


[4080] params:
[4080] 0:
[4080] 1:
[4080] 0: systeminfo
[4080] 1: items
[4080] 2: 0
[4080] 3: 200
[4080] 4: menu:1
[4080] 5: useContextMenu:1
[4080] method: slim.request
[4080] id: 1
[4080] result:
[4080] base:
[4080] actions:
[4080] add-hold:
[4080] params:
[4080] menu: systeminfo
[4080] itemsParams: params
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: playlist
[4080] 2: insert
[4080] player: 0
[4080] add:
[4080] params:
[4080] menu: systeminfo
[4080] itemsParams: params
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: playlist
[4080] 2: add
[4080] player: 0
[4080] play:
[4080] params:
[4080] menu: systeminfo
[4080] itemsParams: params
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: playlist
[4080] 2: play
[4080] player: 0
[4080] go:
[4080] params:
[4080] menu: systeminfo
[4080] itemsParams: params
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: items
[4080] count: 5
[4080] window:
[4080] item_loop:
[4080] 0:
[4080] playAction: go
[4080] actions:
[4080] add:
[4080] params:
[4080] menu: systeminfo
[4080] item_id: f85db2c7.0
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: items
[4080] play:
[4080] params:
[4080] menu: systeminfo
[4080] item_id: f85db2c7.0
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: items
[4080] go:
[4080] params:
[4080] menu: systeminfo
[4080] item_id: f85db2c7.0
[4080] cmd:
[4080] 0: systeminfo
[4080] 1: items
[4080] addAction: go
[4080] text: SqueezeCenter
[4080] 1:
..etc.

So, there are the entries (from a 7.3.4 server). And the next step? How do I "walk" that 2nd level menu?

Anyway, I guess my first task is to replicate in perl what I've done so far here in javascript.

Thanks for your help. I've resisted going down the JSON path for far too long.

mherger
2010-01-19, 00:28
> OK, what's already being used in SBS? I see that SBS's CPAN contains
> JSON::XS. Is that the correct module to use?

yes

> Are there any existing SBS
> perl modules that are making calls to jsonrpc.js that I could crib
> from?

Not in SBS itself, but Slim::GUI::ControlPanel::serverRequest() comes close.

> GUI/ControlPanel.pm

Yeah, that's the one.

> [4080] item_loop:
> [4080] 0:
> [4080] playAction: go
> [4080] actions:
> [4080] add:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] play:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] go:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] addAction: go
> [4080] text: SqueezeCenter
> [4080] 1:
> ..etc.

item_loop are the menu items. You'd have to parse for its name (text: SqueezeCenter, nowadays "Squeezebox Server") - which can be localized too. It's probably simplest to just get the first item. Should work now, but can't guarantee :-(.

What sounded like a good idea turns out to be painful to implement. But I lack a better plan.

> So, there are the entries (from a 7.3.4 server). And the next step?
> How do I "walk" that 2nd level menu?

systeminfo items 0 10 id:be32d22a.0

would give you the next level for the above item.

--

Michael

MrSinatra
2010-01-19, 00:28
gharris, i think you scared andy away!

but i know you meant everything in all good humor! gave a good chuckle.

ps. i firmly bow at the altar of andy, he's the chase utley of logitech imo.

gharris999
2010-01-19, 09:11
Yeah, that's the one.

> [4080] item_loop:
> [4080] 0:
> [4080] playAction: go
> [4080] actions:
> [4080] add:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] play:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] go:
> [4080] params:
> [4080] menu: systeminfo
> [4080] item_id: f85db2c7.0
> [4080] cmd:
> [4080] 0: systeminfo
> [4080] 1: items
> [4080] addAction: go
> [4080] text: SqueezeCenter
> [4080] 1:
> ..etc.


systeminfo items 0 10 id:be32d22a.0

would give you the next level for the above item.
Ummm...sorry to be so dense, but in the above output, where does "be32d22a.0" come from?

mherger
2010-01-19, 09:23
>> systeminfo items 0 10 id:be32d22a.0
>>
>> would give you the next level for the above item.
> Ummm...sorry to be so dense, but in the above output, where does
> "be32d22a.0" come from?

Ooops... that's what I got when testing on my system. You should use whatever you get returned in the first query.

gharris999
2010-01-19, 10:01
Ok, given output from the 1st query of:


...
[4296] item_loop:
[4296] 0:
[4296] playAction: go
[4296] actions:
[4296] add:
[4296] params:
[4296] menu: systeminfo
[4296] item_id: 527ddb16.0
[4296] cmd:
[4296] 0: systeminfo
[4296] 1: items
[4296] play:
[4296] params:
[4296] menu: systeminfo
[4296] item_id: 527ddb16.0
[4296] cmd:
[4296] 0: systeminfo
[4296] 1: items
[4296] go:
[4296] params:
[4296] menu: systeminfo
[4296] item_id: 527ddb16.0
[4296] cmd:
[4296] 0: systeminfo
[4296] 1: items
[4296] addAction: go
[4296] text: SqueezeCenter
..etc.

I construct a 2nd level query using the value from response.result.item_loop[0].actions.go.params.item_id:


[4296] string: '{"id":1,"method":"slim.request","params":["",["systeminfo", "items", 0, 10, "id:527ddb16.0"]]}'

..which yields output of:


[4296] result:
[4296] count: 5
[4296] loop_loop:
[4296] 0:
[4296] id: ce8b4b3d.0
[4296] name: SqueezeCenter
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 1:
[4296] id: ce8b4b3d.1
[4296] name: Library Statistics
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 2:
[4296] id: ce8b4b3d.2
[4296] name: Player Information
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 3:
[4296] id: ce8b4b3d.3
[4296] name: Folders
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 4:
[4296] id: ce8b4b3d.4
[4296] name: SqueezeCenter Log File
[4296] isaudio: 0
[4296] hasitems: 1
[4296] title: Information

..and having constructed a 3rd level query from the value of response.result.loop_loop[0].id:


[4296] string: '{"id":1,"method":"slim.request","params":["",["systeminfo", "items", 0, 10, "id:ce8b4b3d.0"]]}'

..this yields more or less the same output:


[4296] result:
[4296] count: 5
[4296] loop_loop:
[4296] 0:
[4296] id: 37a456b4.0
[4296] name: SqueezeCenter
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 1:
[4296] id: 37a456b4.1
[4296] name: Library Statistics
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 2:
[4296] id: 37a456b4.2
[4296] name: Player Information
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 3:
[4296] id: 37a456b4.3
[4296] name: Folders
[4296] isaudio: 0
[4296] hasitems: 1
[4296] 4:
[4296] id: 37a456b4.4
[4296] name: SqueezeCenter Log File
[4296] isaudio: 0
[4296] hasitems: 1
[4296] title: Information

So I'm caught in a loop. What am I doing wrong here?

erland
2010-01-19, 12:19
I construct a 2nd level query using the value from response.result.item_loop[0].actions.go.params.item_id:


[4296] string: '{"id":1,"method":"slim.request","params":["",["systeminfo", "items", 0, 10, "id:527ddb16.0"]]}'


Try to use item_id instead of id:


[4296] string: '{"id":1,"method":"slim.request","params":["",["systeminfo", "items", 0, 10, "item_id:527ddb16.0"]]}'


This at least seems to return the information when you use it over CLI interface so I'm guessing it will also work over JSON.

gharris999
2010-01-19, 13:57
Try to use item_id instead of id:


[4296] string: '{"id":1,"method":"slim.request","params":["",["systeminfo", "items", 0, 10, "item_id:527ddb16.0"]]}'


This at least seems to return the information when you use it over CLI interface so I'm guessing it will also work over JSON.
Yep. That's it. Thank you!

Final question (for now) for all of you:

Is there any utility in making the XMLHttpRequest.open synchronous (and perhaps using a setTimeout() call to handle error conditions) for simplicity's sake? Or is asynchronous *ALWAYS* the way to go with JSON/RPC?

pippin
2010-01-19, 14:13
The latter.

mherger
2010-01-19, 14:31
> Is there any utility in making the XMLHttpRequest.open synchronous

That's javascript? Are you trying to poll information from other servers using the web UI? I'm not sure this will work, for security reasons.

> (and perhaps using a setTimeout() call to handle error conditions) for
> simplicity's sake? Or is asynchronous *ALWAYS* the way to go with
> JSON/RPC?

I don't know. I've never looked at the gory details of that class. Use a JS framework which does all the hard work for you we're using ExtJS.com for the Default skin, prototypejs.org for the other skins. Both offer you simpla Ajax.request() methods to do a call and have it trigger some callback once it's got the data.

--

Michael

erland
2010-01-19, 15:34
Is there any utility in making the XMLHttpRequest.open synchronous (and perhaps using a setTimeout() call to handle error conditions) for simplicity's sake? Or is asynchronous *ALWAYS* the way to go with JSON/RPC?

I'm not sure I understand where this code is.

If you are inside a perl plugin, take a look at LWP::UserAgent in the bundled MusicMagic plugin. Of course, any synchronous call will ruin any chances of getting the plugin onto the recommended risk, so if that's important you need to go the asynchronous route.

gharris999
2010-01-19, 17:27
While I've been prototyping this code in javascript, it will ultimately be in perl in a SBS plugin.

Re asynchronous vs. synchronous: Here's the goal:


Get a list of *OTHER* SBSs via Slim::Networking:: Discovery::Server::getServerList()
Identify which of those servers may be a TinySC on a Fab4 using the pointers that Michael and the rest of you have given me in this thread.
Present the user with a drop-down SELECT list on the plugin's settings page with any Fab4/TinySC positioned at the top of the list.


The user will select a *OTHER* sever entry so that, upon shutdown of *THIS* server, all connected client players will get pushed/connected to the *OTHER* server.

Using all of your pointers, I can identify a given server as a Fab4/TinySC if: Operating system ~= m/SqueezeOS/ && Platform Architecture ~= m/arm\-linux/

The design question for me is this:

What is the best way to suspend execution in my settings' page handler while waiting for the aforementioned asynchronous calls to complete?

Obviously, I'm 'thinking' synchronously here.

I'm guessing that a better approach is to store the list of *OTHER* servers in my prefs. When the asynchronous call updates the list in the prefs, my $prefs->setChange function gets called. But how do I then force a refresh of the settings page so that the SELECT list gets repopulated?

erland
2010-01-19, 17:53
What is the best way to suspend execution in my settings' page handler while waiting for the aforementioned asynchronous calls to complete?

Obviously, I'm 'thinking' synchronously here.

I'm guessing that a better approach is to store the list of *OTHER* servers in my prefs. When the asynchronous call updates the list in the prefs, my $prefs->setChange function gets called. But how do I then force a refresh of the settings page so that the SELECT list gets repopulated?

Someone still has to convince me why synchronous calls is a such a bad idea when used from setting pages. I completely understand that it's a bad idea for things that happens when music is normally playing, but I'm pretty sure most people doesn't care if there are some disturbances when I'm configuring the system.

Some of the SQL statements that are executed in my plugins on the recommended list are a lot slower than some of the synchronous HTTP calls I make in some of my other plugins which didn't get onto the recommended list partly due to this. Of course, I don't really care if they are on the recommended list or the other list.

Unless you really want to get on the recommended list or you really want to do everything to avoid music disturbances, I'd still suggest the LWP::UserAgent route with synchronous call, it should make it a lot easier.

andyg
2010-01-19, 18:04
What if the server you're trying to make a sync HTTP call to is down, or the user is having network problems. This will completely block everything for 3 minutes (default LWP timeout).
This then causes forum posts like "My music keeps re-buffering". Please just don't, writing async code is not that hard, even in settings pages. There are plenty of examples.

erland
2010-01-19, 18:17
What if the server you're trying to make a sync HTTP call to is down, or the user is having network problems. This will completely block everything for 3 minutes (default LWP timeout).
This then causes forum posts like "My music keeps re-buffering". Please just don't, writing async code is not that hard, even in settings pages. There are plenty of examples.

It's not really the topic of this thread, but is there any way to do asynchronous SQL statements ?

Or is the only way to try to divide them into smaller chunks and use something like Slim::Utils::Scheduler to make sure other activities are allowed to execute in-between ?

I have a lot more complaints due to SQL statements that takes time in my plugins compared to the HTTP calls, probably mostly due to that the SQL statements are used while music is playing while the HTTP calls are used when configuring.

Most (but not all) of the SQL statements are related to the problem that there is no way to hook into the scanner process with third party code so I have to execute them in the main SBS process which affects other activities.

andyg
2010-01-19, 18:38
Yeah unfortunately async SQL requires a design decision to be made up front when developing something, and it's next to impossible to put it in after the fact, especially when we have SQL going on all over the place. Scheduler is a good choice, I have recently improved it quite a bit (in embedded) so it works better.

gharris999
2010-01-19, 23:58
What if the server you're trying to make a sync HTTP call to is down, or the user is having network problems. This will completely block everything for 3 minutes (default LWP timeout).
This then causes forum posts like "My music keeps re-buffering". Please just don't, writing async code is not that hard, even in settings pages. There are plenty of examples.
Not that I'm advocating synchronous calls, but what would you regard as a "safe" timeout value for LWP::UserAgent when calling against SBS?

And assuming that I can go the async route, do you have any tips for me as to how to force a refresh of a settings page?

mherger
2010-01-19, 23:59
> Using all of your pointers, I can identify a given server as a
> Fab4/TinySC if: Operating system ~= m/SqueezeOS/ && Platform
> Architecture ~= m/arm\-linux/

....and second I'd list all those with Architecture !~ /(?:x86|x64)/ or similar.

> What is the best way to suspend execution in my settings' page handler
> while waiting for the aforementioned asynchronous calls to complete?

If a page handler returns undef, then the browser will keep waiting. You can use this to trigger an async call to do what you want to do. The callback function to that async request would then return the HTML data to the the browser.

This is roughly how it's being done (untested code...):

sub handleSettingsPage {
my ($client, $params, $callback, $httpClient, $response) = @_;

my $http = Slim::Networking::SimpleAsyncHTTP->new(
\&successCallback,
\&errorCallback,
{
client => $client,
params => $params,
callback => $callback,
httpClient => $httpClient,
response => $response,
timeout => 30
}
);


return undef;
}


sub successCallback {
my $http = shift;
my $params = $http->params();

# build your html
my $content =...

$params->{callback}->($params->{client}, $params->{params}, $content, $params->{httpClient}, $params->{response});
}


> Obviously, I'm 'thinking' synchronously here.

Yeah, it took me a while to get around it, but I needed it for the AlbumReview/Biography plugins, which can be pretty slow.

--

Michael

erland
2010-01-20, 00:16
If a page handler returns undef, then the browser will keep waiting. You can use this to trigger an async call to do what you want to do. The callback function to that async request would then return the HTML data to the the browser.

Thanks, this was news to me.

I wonder if it would be possible to somehow use this to run SQL statements in the background ?

I'm guessing the page will be displayed as soon as I call the callback the first time ?

It might be possible to divide the SQL in smaller chunks, use Slim::Utils::Scheduler, buffer the result for each chunk and make the call to the callback when the last chunk has been executed ?

gharris999
2010-01-20, 00:17
> Using all of your pointers, I can identify a given server as a
> Fab4/TinySC if: Operating system ~= m/SqueezeOS/ && Platform
> Architecture ~= m/arm\-linux/

....and second I'd list all those with Architecture !~ /(?:x86|x64)/ or similar.

> What is the best way to suspend execution in my settings' page handler
> while waiting for the aforementioned asynchronous calls to complete?

If a page handler returns undef, then the browser will keep waiting. You can use this to trigger an async call to do what you want to do. The callback function to that async request would then return the HTML data to the the browser.

This is roughly how it's being done (untested code...):

sub handleSettingsPage {
my ($client, $params, $callback, $httpClient, $response) = @_;

my $http = Slim::Networking::SimpleAsyncHTTP->new(
\&successCallback,
\&errorCallback,
{
client => $client,
params => $params,
callback => $callback,
httpClient => $httpClient,
response => $response,
timeout => 30
}
);


return undef;
}


sub successCallback {
my $http = shift;
my $params = $http->params();

# build your html
my $content =...

$params->{callback}->($params->{client}, $params->{params}, $content, $params->{httpClient}, $params->{response});
}


> Obviously, I'm 'thinking' synchronously here.

Yeah, it took me a while to get around it, but I needed it for the AlbumReview/Biography plugins, which can be pretty slow.

--

Michael

That's very, very clear. Thank you.

mherger
2010-01-20, 00:23
> I'm guessing the page will be displayed as soon as I call the callback
> the first time ?

The page will be served whenever you call the $params->{callback}->() from inside the async callback... quite a bit confusing, I know. I wouldn't have known this any more without looking at my own code. Andy might do better :-).

> It might be possible to divide the SQL in smaller chunks, use
> Slim::Utils::Scheduler, buffer the result for each chunk and make the
> call to the callback when the last chunk has been executed ?

I've never used the scheduler myself...

--

Michael

andyg
2010-01-20, 06:11
Thanks, this was news to me.

I wonder if it would be possible to somehow use this to run SQL statements in the background ?

I'm guessing the page will be displayed as soon as I call the callback the first time ?

It might be possible to divide the SQL in smaller chunks, use Slim::Utils::Scheduler, buffer the result for each chunk and make the call to the callback when the last chunk has been executed ?

Sort of, you can't run SQL in the background (that would require a separate process) so every statement will still block the server. But splitting it up into chunks will work if that's possible for you to do. In a web handler you can only call the callback once, when you're done and want to display the page.