How It Works
Lazy Encoding Principles
The principle of lazy encoding is to approximate any piece of full-alphabet text with the key number on the remote control key for each alphabetic character. Thus, "ABBA" becomes "2222" and "PINK FLOYD" becomes "7465035693" (spaces are encoded as zeros, and punctuation is removed).
Once you have lazy versions of strings that need to be searched (such as artist names, song and album titles), then searching with this lazy form is trivially easy.
The SqueezeCenter music library is stored in an SQL database - either your own MySQL instance or SqueezeCenter's own embedded instance - with separate tables for artists, albums and tracks etc.
The SqueezeCenter database schema conveniently includes 'customsearch' columns for artists, albums, genres and songs (added at my request, and I'm very grateful for it), that the core SqueezeCenter software doesn't use at all. This gives us a handy place to store an alternative lazy-searchable version of the text.
Because there isn't a concept of keywords within SlimServer there is no customsearch column available for the sole use of keywords; instead, the customsearch column for tracks is used. The customsearch column in the tracks table holds both a standard lazified version of the track title, and a lazified version used in keyword searching - these are separated by a pipe character ("|"), so one or the other can be easily searched for using wildcards. The part that holds the keyword search holds a concatenated version of the album title, track title, and all of the artists associated with that track (taking into account user preferences for what should be included for keyword searching), so searching for keywords can then easily match across all item types.
How the Database is Updated
Unfortunately, SqueezeCenter doesn't provide any means for a plugin to intercept and modify the data that is being stored in the database during a music scan. Instead, the Lazy Search plugin registers a subscription for the 'rescan done' notification, and uses that to trigger post-processing of the database. This post-processing goes back through the database and encodes each of the standard 'search' versions with their lazy counterparts, stored in the 'customsearch' columns.
The actual modification of the database is done in two separate parts:
- All of the artists, albums, genres and tracks are searched and the ids of those which have not yet been converted are added to a hash. As this will make use of database indices to perform the search this actually completes quite quickly, and at the end the hash is effectively a 'job queue' of database entries that need modification.
- A background task is scheduled that SqueezeCenter will call whenever it is effectively idle. This background task will convert as many of the items recorded within the hash as it can before a short timer runs out - when it does the task returns with an indication it wants to be called again (ie there's more work to do). Using a task in this way ensures that SqueezeCenter will remain responsive despite potentially many thousands of database modifications being made.
There is additional work done when the plugin is initialised (ie when it's enabled for the first time, or whenever the server is restarted), such as registering modes and defaulting preferences if they've never been set before.
The plugin provides a two-level menu in the same way as the normal search button. The top-level menu consists of "Artists", "Albums", "Genres", "Songs" and "Keywords" and is displayed and chosen using a mode called "PLUGIN_LAZYSEARCH2.categorymenu" derived from the standard "INPUT.Choice" built-in mode. A new mode is created for this so that the plugin can tell if it is in this menu when SEARCH is pressed (as it pops back into the original search menu).
Choosing any of these items pushes into a new mode registered for this plugin called "PLUGIN_LAZYSEARCH2.browsemode". This mode is a customisation of the standard "INPUT.Choice" built-in mode created by copying the functions from the standard mode and then overwriting and adding some of the entries. This can be thought of as providing a crude form of subclassing within SqueezeCenter. The SEARCH button is remapped to this newly-registered mode to make it easily accessible.
The new mode registered includes quite a bit of custom functionality, including:
- Number button pushes are intercepted and added to the search text held.
- The PLAY, ADD and INSERT buttons are intercepted and cause the same behaviour as the normal "browse" mode would on each type of search result.
- The LEFT button is intercepted and causes a character to be removed from the current search text (subject to a user preference).
To improve responsiveness, the searching is not performed on each number button push. Instead, each number press registers a timer (cancelling an outstanding one if it is present), scheduled for a short time in the future. This timer is identified by the player address so that multiple players can be supported without interference. When that timer fires the search is performed and the mode popped and pushed to refresh the search result display with the new results. Some additional logic makes sure that an outstanding timer is cancelled if the search mode is left prior to the timer firing, and to ensure a minimum length of search text has been provided before the search is performed (although the user can still force a search 'early' if they press SEARCH).
A globally-scoped hash is used to store current state for the plugin. This hash is keyed on the player address so that, again, multiple players do not interfere with each other.
Other Aspects of the Plugin
There's quite a bit to the plugin, but the best guide is to read the source. I've put a lot of comments in and so I hope it's fairly readable.
A summary of the other aspects of the plugin you may want to examine the source for include:
- Maintaining state per-player using a hash.
- Using a subscription to receive a notification that the normal SqueezeCenter database scan has been completed (and so the plugin can start to encode the lazy search versions).
- Using timers and tasks to complete large amounts of work in the background while still maintaining a responsive SqueezeCenter web and player interface.
- Using the standard browsedb and track information modes (entered by pressing RIGHT on search results).
- Database access using the schema classes, including joins and complex criteria.
- Adding and removing request subscriptions to initiate plugin functionality in response to things the user does with SqueezeCenter.
- Plugin preferences available from the plugins page of the SqueezeCenter web inerface.
- Playlist management (used to action the PLAY, ADD and INSERT' buttons).
- Mixer integration (used to support MusicIP).
- Support for single and 2-line display modes (working slightly differently for each).
- Embdedded string translation map (to support multiple languages within the player HCI).