Simon Fell > Its just code > Radio Extensibility
Saturday, January 19, 2002
Last week i said i want to tweak the date/time stamp on the weblog posts to include the currently playing track from WinAmp.
After shipping PocketSOAP 1.2.2 this morning I starting hacking Radio to do this.
Although I've used Manila and Frontier before, this is the first time I've a taken a real look at the scripting facilities of Radio/Frontier. As it took a while to find the right set of incantations, i thought i'd document them here for future Radio scripting newbies.
So, first up I looked through all the prefs, found the Item template, noticed that this includes generating the timestamp line, score !. I tweaked this, and did a new post, looks good, my changes appear, but the Item template is evaluated at rendering time, so any change to it is applied to all the items on the page, not just the new ones. So if you alter the item templete to directly show the current track, the same track will appear on each item, rather than the previous items remembering what was playing at that time.
Hmmm, we really need to store the trackname along with the item, rather than just trying to do it in the item template, so where are the items stored ?. I originally thought they were text files in the www drive, I've seen "Radio has a file based CMS" stated by a few folks, so the items are disk files right ? I trawled the www directory, and no sign, just the little templates that call <%radio.macros.viewWeblog ()%>. OK, open radio, look in Windows, ahh, there a weblogData.root sounds promising
Quickly i found under posts a list of items, each with a text and when field, bingo, here's our weblog items. That makes it easier, we can add our own data to each post, i expand the last post, goto the Table menu, New Scalar, string, and create a string field called tunz, and stick some text in the value.
Now, we need to go back to the item template, and modify it to try and find a tunz field for the post, and display it in the right place. First i looked in the help at the standard macros to see if any of they gave me the post object, no luck :(. Time to trawl the web, I found one of Dave's tutorials, Going crazy with macros, making a random deciphering of the script in there, it appears to walk the list of posts in the posts table, so sounds like a good start, lets grab that.
<% local (s = ""); local (adrblog = radio.weblog.init ()); local (adrposts = @adrblog^.posts, i); for i = sizeof (adrposts^) downto 1 { local (adrpost = @adrposts^ [i]); if (adrpost^.when >= monthStart) and (adrpost^.when <= monthEnd) { s = s + "<p>" + adrpost^.when + ": " + string (adrpost^.text) + "</p>" } }; return (s) %>
adrposts looks like it refers to the weblogData.posts table, so we need to extract the current item from that table, rather than walking the whole list. Back to the list of macros, there a postNum macro that refers to the postNum this matches the number in the posts table, but how do we use the macro inside our script ?, lets try the obvious
<% local (adrblog = radio.weblog.init ()); local (adrposts = @adrblog^.posts, i); local (adrpost = @adrposts^.[postNum] ) ; %>
Nope, no good, that gives an error, so does macros.postNum and radio.macros.postNum (guesses based on looking at the frontier docs)
In desperation more than anything else, i just nested the macro inside the existing script block
<% local (adrblog = radio.weblog.init ()); local (adrposts = @adrblog^.posts); local (postNum = "<%paddedItemNum%>" ) ; local (adrpost = @adrposts^.[postNum]) ; %>
I was shocked to find out that this actually works, but hey, progress is good :)
Now we just need to check to see if a tunz field exists and if it does, pull the value out to display, if it doesn't, show some default, looking a few other examples, i found the defined function, so we end up with
<% local (adrblog = radio.weblog.init ()); local (adrposts = @adrblog^.posts); local (postNum = "<%paddedItemNum%>" ) ; local (adrpost = @adrposts^.[postNum]) ; local (tunz = "[no music !]" ) ; if defined ( adrpost^.tunz ) { tunz = adrpost^.tunz ; }; return tunz ; %>
Cool, it works, i add tunz fields to a couple more posts in the table, then post a new weblog item to get the page re-rendered, and my entries appear !.
That's the display end taken care off, now we need to somehow detect that there's been a post and create the tunz field on that post item. More trusty Google work turns up another of Dave's docs, talking about callbacks for post/publish/delete, exactly what we're looking for.
No example in Dave's doc, so start digging, Dave says all the scripts in user.radio.callbacks.postItem get called when a user posts an item. Open Radio back up, radio.root, expand the tree user -> radio -> callbacks -> postItem. Just one item in there "Item #1" with a value (nil). Looks like a place holder, lets rename it and create our script. I rename it to grabTunz, and set the type to script. Then i put in the following script
on post ( adrPost ) local ( np = dll.call("G:\\Source\\vc\\nowPlaying\\Debug\\nowPlaying.dll", "NowPlaying") ); adrPost^.tunz = np ;
We'll come back to the dll.call thing later. Hit compile, no errors, back to the homepage, post a new item, hmmm, nothing. I look around some of the other scripts in radio.root and notice that their names in the table match the names in the script, perhaps they're suposed to be the same, open the script up, and change it to
on grabTunz ( adrPost ) local ( np = dll.call("G:\\Source\\vc\\nowPlaying\\Debug\\nowPlaying.dll", "NowPlaying") ); adrPost^.tunz = np ;
Hit compile again, no errors, back to the homepage, post a new item, wow ! bingo ! it works !, the timestamp line on the new post includes the currently playing winamp track. Success.!
The DLL
As I'm sure you've noticed the the last script magically obtains the current track from WinAmp. I conviently had some C# code from Jason Whittingtons MSNMangler [which updates your MSN IM Name with the current WinAmp track], my original plan was to try and re-use that by using the COM interop features in .NET. But Radio can only indirectly call COM objects, it can run VBScript, which can make COM calls, so i could of piped it up that way, but i also noticed that Radio can call DLL's directly, and that looked a bit cleaner. I whipped up the DLL in VC6, following the steps in the Writing Frontier DLLs article. In order to find the current track from WinAmp, the code simply walks all the top level windows, look at the window titles, and extracts the track name part from it, there's probably a way you can plug-in winamp directly to find it as well, but this works fine for now. You can grab a copy of the built DLL if you want to run this yourself.