Bugzilla – Bug 6930
Extensible Track Information Interface
Last modified: 2009-09-08 09:20:15 UTC
(from original forum post) The problem: Recently I've been playing with the Lyrics plugin. Whilst it's very nice (really, it is), it's somewhat frustrating to have to go to the Extras menu to get to lyrics. It doesn't seem right. It should be a property of the track information. So I wanted to make it possible for it to be on the track information page. The TrackInfo.pm plugin provides this menu, and has a number of hacks in it. It has these hacks because the menu is fixed, and non-extensible by external plugins. The hacks I'm referring to are for the AudioScrobbler plugin and the Favorites selection. These should really be in the relevant plugins. Briefly I toyed with the idea of hacking the lyrics support in. But that would be bad and I would hate myself for it. Instead I wanted to make a menu that could be extended by plugins. The audioscrobbler code could move to the audioscrobbler plugin and the favourites code to its plugin. And the lyrics plugin could register to extend the menu with a Lyrics link. Other plugins, like the biography plugin, or the album review plugin could provide other information. The world would be bright and smiley and there would be peace. My solution: My solution was simple. Remove the hardcodedness of the information items and create a list of registered 'information providers'. Each information provider can add things to the menu, hopefully which are relevant to the track. The providers are managed in a simplified form of dependency list. Each provider can be 'before' or 'after' another named provider, or they may be a member of a group ('isa'). For example, the 'contributors' provider is 'after' the 'title' provider. This way, plugins can place their items in logical positions in the tree. For example, the biography plugin might place itself 'after' the 'contributors'. The grouping of items allows items to be placed into set groups where the order isn't relevant, for example the sample rate and bit rate can all go in a 'fileinfo' group. It doesn't matter what order they appear (although for consistency, they appear in alphabetical order if no other order is defined). This was how I intended to make the system work, with multiple clients registering information providers, and each provider giving new menu items as it saw fit. Because I understand that SlimServer is used in a larger way in SqueezeNetwork, it's obviously important that the dependency calculations weren't performed for every operation - after all they stay the same once things have been registered. So, I would cache the order once we'd worked it out and only recalculate when things changed. That was the design. The implementation: First, I wanted to prototype the code. To see that it was actually going to work in the way that I wanted it to and didn't mean that I ended up writing a lot of ugly code. The implementation was amusing, but not too difficult. I took the basics of the list that SlimServer already had and mocked up a prototype which added these things to a set of lists and worked out an order. The prototype was a success; you can find the code at : [url]http://usenet.gerph.org/SlimServer/TrackInfo/testlist.txt[/url] and the test example code generates the following output : [url]http://usenet.gerph.org/SlimServer/TrackInfo/testlist-output.txt[/url] Basically this is demonstrating the the list ordering works as desired, with each item being ordered correctly wether following one another, or within groups. Three 'top level' groups are used - 'top', 'bottom' and 'middle'. At the moment I've hung almost everything off 'top'. Essentially it means that items can be placed in absolute positions as well as relative ones. So if a plugin always wants to appear at the top of the menu it always specifies 'after' => 'top' (or possibly 'isa' => 'top', depending on the type of behaviour they want). Once the prototype was seen to work and that the coding isn't too ugly, I moved the main functions into the TrackInfo.pm source, and replaced all of the inline 'if' statements with if statements wrapped in function calls. This makes the code a little larger, but more manageable. I don't like putting inline code inside the registration functions because it feels more messy - this way the ordering information on registration is kept apart from the implementation. In doing this I also noticed that the 'ReplayGain' and 'AlbumReplayGain' items were separated by the 'Rating'. This looked stupid, so I put the two ReplayGains together, and left Rating following them. Such things are easier when you're dealing with relative positions, I think. Having implemented all the functions, I began to look at the AudioScrobbler hack. Because of the way the code is structured, this could be lifted almost wholesale from the TrackInfo.pm code and moved into the AudioScrobbler plugin. This has two uses - 1) it stops the collusion between AudioScrobbler and TrackInfo, and 2) it gives other plugin authors a useful guide for 'Oh, *that* is how you add items to the TrackInfo menu'. I moved on to the Favourites item. This has hacks in 2 ways - firstly, it uses the magic string 'FAVORITES' to mean 'change me when you come to work out what line to draw'. This seemed a mess, so I made the line that is used accept a coderef. If you have a coderef in place of text, the code will be called to work out the text to use. Secondly, there was a special case of the 'type' which performs different operations when the item is selected. This was moved to be a coderef as well. At this point, I couldn't work out whether the Favourites handling should be in Slim::Utils::Favorites or Slim::Plugin::Favorites. So I didn't move it. It would be trivial to do so in the future. The diffs for my changes can be found here : [url]http://usenet.gerph.org/SlimServer/TrackInfo/registered-info-entries.diff[/url] Possible uses: I've already mentioned a few possible uses, but here's a quick list of things I can think of (which includes those things that already exist) : * AudioScrobbler - ban / love this track * Album review (an item below the album name?) * Artist biography (an item below the contributors?) * Lyrics (an item at the bottom of the list?) * Favorites selection (similar to ratings, so maybe group these?) * Ratings (changing the ratings for a track by a sub-menu item ?) * Genre add to 'random mix', or remove from 'random mix'? * Other meta data, like 'Involved people', or a list of the instruments the band members play ? * Label released under I'm sure there are other uses, but those are only the ones I can think of right now. Using the interface: As I have mentioned above, the interface has an example in the AudioScrobbler plugin which should be pretty clear. I've tried to document the functions I've got so that they're readable (doxygen style, because that's what I use at work - apologies as that's not the prevalent style in SlimServer). Essentially a plugin would register a new information provider when it initialises : registerInfoProvider('<name>', ( <position>, 'func' => \&<function-to-call> 'private' => <some private value> ); where <name> is the name this information provider is to be given (so that it's available to others to be positioned relative to). <position> may be one of : 'before' => '<thing-which-should-follow-this>' or 'after' => '<thing-which-should-precede-this>' or 'isa' => '<thing-which-this-is-a-member-of>' <function-to-call> is a function which takes : $client (the client this refers to) $private (any private value, so that the function can be reused if necessary) $url (the url object for the track item) $track (the track object for the track) <private> can be omitted and is intended to allow same function to provide multiple types of data. For example, to place a 'change-rating' entry after the regular Rating item, you would use : Slim::Buttons::TrackInfo::registerInfoProvider( 'change-rating', ( 'after' => 'rating', 'func' => \&infoChangeRating ); The function which is called might be something like this : sub infoChangeRating { my ($client, $private, $url, $track) = @_; Slim::Buttons::TrackInfo::addInfo($client, "Set track rating", \&functionToSetRating); } Any Slim::Buttons::TrackInfo::addInfo calls which are made will accumulate menu items. The text string above is untranslated. Obviously you'd use the translated form (but it's easier to write like that for an example). The function reference given above will be called when the user presses 'right' over the item. It will be called passing the $client parameter as its only variable. If new menus are required, they can be pushed into in the usual way. If the text string, above, was a coderef instead of a string, the code would be called, passing the $client parameter. The function called should return the string to display. Epilogue: That about covers what I wanted, why I was doing it, how I planned to do it, and how to use it. I didn't implement the necessary bits to hook in to the Lyrics plugin. I reckon the first hurdle is getting something like this into the core and then I can worry about updating the plugins to use it. I don't know if this sort of functionality is desired by Logitech, but it seems to me that with everything else being so extensible, the TrackInfo menu has been missed out. Also, it seems to me that if the information is managed in a structured way then passing this track information in the same form to the Jive remote becomes far, far simpler, which is a nice bonus as well. Anyhow, I hope that the developers will consider the patch for inclusion.
Created attachment 2809 [details] Proposed change to the TrackInfo menu
When applying this to the core, remember to also add similar support to: Jive interface in: Slim::Control::Queries songinfoQuery Web interface in: Slim::Web::Pages addSongInfo The web interface additionally also needs a rework of songinfo_details.html which currently hard code the information that should be shown instead of reading it from a hash/array.
At this late point, we'll have to punt this post 7.0. Very nice work, though. Erland: your point is good. This interface should make it relatively easy to add stuff to all the song info screens/pages at once: Jive/Web/Player. Who should I assign this bug to?
I'll take it.
As this is a feature, bumping to 7.1.
This patch is a great start, thanks. My current plan is to change this up a bit so that the thing that provides the menu structure is more generic and taken out of Slim::Buttons, and returns OPML or our internal hash representation of OPML. This way the same menu can then be rendered on all 3 interfaces without duplicating any of the code. And each plugin only has to provide the same menu structure to be available on any UI. I'm not sure this will work for all plugins though, if some are player UI-specific and change the meaning of buttons, etc. Will have to keep in mind a way to define menus for only a particular UI.
I'm glad you think it's useful; even if it's not quite in this form, it would be handy to provide the information structures in a more extensible way like this. I couldn't work out how to get the same thing into the Jive interface when I was looking at it, so if they can be unified though the OPML or internal representation that would be cute. I hope the patch hasn't become too stale with time.
*** Bug 6619 has been marked as a duplicate of this bug. ***
Started working on this on the 7.1 branch 'trackinfo', if you want to take a look. Player UI is all that's done so far. See change 20909.
(In reply to comment #9) > Started working on this on the 7.1 branch 'trackinfo', if you want to take a > look. Player UI is all that's done so far. See change 20909. > Andy, I just had a quick look at this change and I saw that you had added a 'db' type to the OPML menus that will work for all standard SqueezeCenter menus. If possible it would be great if there also was a way for a plugin to hook in its own mode. As I understand 'db' will enter the 'browsedb' mode, but a plugin typically wants to enter a completely different mode. Today I've re-implemented the trackinfo menu in the Custom Browse plugin as a temporary solution, but when this enhancment is finished I would like to replace my implementation by just hooking in Custom Browse into this new solution. However, to be able to do that I need to be able to run plugin code to for example push into my own player mode. If this isn't already possible with your current implementation, it would be great if was in the final solution. So if, you need special cases to handle the standard browse menus like the 'db' type, make sure to implement them so they also can be used for third party plugin based menus.
You shouldn't think of 'modes' or anything player UI-specific. Plugins should provide generic menus in the internal OPML format so they will work in player UI, web UI, and SBC UI. Hopefully it will make more sense in a few days when it's more complete. The type='db' was added so I wouldn't have to port all of the music library to this menu structure. :) I want to do that at some point to unify that mess of duplicated code, but not right now.
Added in 7.1 change 21078. Please see Slim::Menu::TrackInfo for documentation.
Oooh, exciting :-) I'll try to have a look this evening when I'm out of work. Thanks for taking on this work :-)
This isn't really fully implemented for Controller or WebUI yet. Reopening and assigning it to myself. Adding Michael to the cc: list
7.1 change 21111 has support for adding "Play This Track" and "Add This Track" SBC/Squeezeplay-only items to the top of new TrackInfo menu (r.i.p. Slim::Control::songinfo) still to do/fix for the Controller: Show Artwork Music IP Add/Delete Favorites
Created attachment 3485 [details] adding some meta data for the web I'd like to add a "web" element to get some additional information for the web UI. To satisfy Dean's & the users' expectation for a decent web UI I'd like to have eg.: - the values without a label - some meta information to be able to group eg. contributors Also I'd like to add the More Info items to the web page, as compared to the other UIs we have a lot of screen estate in the web UI. This would break with the UI as defined on the wiki, but it's worth the inconsistency. This way the user really has all/most of the information he wants to see on one screen.
Created attachment 3486 [details] Almost there... with the above patch & a bit more work on the templates
One more thing: I'd like to be able to do a search on the current title. Pretty often I'd like to know whether I have duplicates or cover versions of a track. This might be an "On SqueezeCenter" item, or (redundancy alert!) a browsable track title item.
I think there's actually some utility to having the track title item in both playerUI and jiveUI, particularly in Browse Music Folder. For example, when I go to Browse Music Folder->...-->Air - 10,000 Hz Legend-->(1st track), it reads: Air - 10000 Hz Legend - 01 - Electronic Performers.mp3 rather than wait for all that text to scroll by on the display, I can just hit right arrow and have it show up on the screen as Title: Electronic Performers. IMO it should be the top item in the player UI and the 3rd item (after Play and Add) in jiveUI.
I'm not sure how this will work, but will it be possible for a third party plugin to add it's own items ? In the web interface this would mean that I for example from the Custom Browse plugin would like to add a link to a performer. To do this I would need to be able to add a link to the song info page with a custom title and a custom url. In the Controller interface I would like to do the same thing, but there it would mean that I would like to add an item that enters a custom menu by executing a custom JSON command implemented by my plugin that returns the menu data. If I understod Andy correctly back in comment #11 everything is supposed to be opml based, but I'm a bit worried regarding the handling of the web interface which doesn't seem to be opml based at all. For example the patch in comment #16. Anyway, I just want to make sure that you guys really make sure this is possible to use by third party plugins to add information to the song information pages/menus which aren't represented by standard SqueezeCenter Slim::Schema:* objects.
The whole point of this is to make it possible and easy for plugins to add items. But you need to start thinking in OPML terms, not things like "JSON actions" :) It would be good if you could try adding something now, so we can uncover any bugs we might not have thought of. Use the existing plugins as a reference. Some of the ugliness right now is because we still have the old browsedb interface we need to support. Eventually I hope it will all be OPML-based.
I think we should allow any plugin to break out of the XMLBrowser using some special keywords. We're already doing this for music browse modes. But this should be extended to be more generice - 'db' needs some hardcoded bits in XMLBrowser and templates. If we used something like pushmode => sub { Slim::Button::BrowseDB::whatever() }, internallink => 'browsedb.html?...' instead, this would ease transition. MusicIP could just point to the existing code instead of trying to mold everything in OPML. This might look like a kludge, as the noble goal is to do all in OPML. But we won't be able to do this in a reasonable time. DB browsing already requires some hard coded workaround. Why not extend it to ease transition?
change 21452 adds play-hold handlers on artist/album/genre items to enable mixer(s), similar to how Slim::Control::songinfoQuery used to do it. I think controller support is complete now. I think that was all that was left here, so moving to RESOLVED. Reopen (and reassign!) as needed...
This bug has now been fixed in the 7.1 release version of SqueezeCenter! Please download the new version from http://www.slimdevices.com if you haven't already. If you are still experiencing this problem, feel free to reopen the bug with your new comments and we'll have another look.
Reduce number of active targets for SC