[% pagetitle = 'Lyrion Music Server Plugins' %] [% techinfo = '1' %] [% lefttoright = '1' %] [% PROCESS helpheader.html %]
This document illustrates the basic framework upon which to build a Plugin or module compatible with the Lyrion Music Server software. The server provides a method to load custom modules at startup which are then made available to the user via remote control menus. Each plugin serves as an area within the server remote menu system, available from within the Plugins menu. Plugin files are stored in the "Plugins" folder which is in the same folder as the server software. For simple plugins, a single file in this folder will suffice. For more complex plugins, you can store an entire set of files in a sub-folder, naming the main plugin file: Plugin.pm. The Plugin interface can provide a very powerful control and has complete access to the functionality of the server. As with the rest of the server, plugin modules are created using the Perl language.
Here are a couple of basic calls that should be made at the beginning of the Plugin code:
use strict;
This tells Perl to do some useful error checking to help avoid the use of undeclared variables, or ambiguous references to functions. All references must be fully qualified (ie Slim::Buttons::Common::popMode() instead of popMode()).
package Plugins::pluginname;
Where pluginname
is the name of the module (which should be the same as the filename, without the .pm extension.) This defines the 'namespace' for the module. It essentially sets up the module as an entity of its own, within the server environment. This allows you to then refer to the functions and variables within this plugin by the full reference Plugins::pluginname::function.
Each level in the server menu hierarchy is considered a "mode" by the server. Each time the server is started, it reads the Plugins directory for modules. Each is added, by name, into a list. Each plugin is given a mode, and a set of functions that are defined within that module. The plugins can each be enabled or disabled at will from the web interface. The plugin module itself must define a mode and functions in a form that the server is expecting. This comes from two functions: sub setMode() and sub getFunctions(). A third subroutine, sub getDisplayName() is required to provide the name that the server should display for the plugin. Simple examples of these three functions are below. We will now build a template based on these main functions.
Example 1
sub setMode { my $client = shift; $client->lines(\&lines); } sub getFunctions { return \%functions; } sub getDisplayName { return "MY_PLUGIN_STRING_TOKEN")} sub strings { return ' MY_PLUGIN_STRING_TOKEN <tab> EN <tab> My Plugin English Name '};
If you wish for your plugin to have a player display interface, then you will need to set up what is called a Mode. The remote buttons are context sensitive, meaning that they can serve different roles when the server is in different modes. The setMode() subroutine defines this mode. Within this subroutine, there must be a definition for $client->lines, which is the text to be displayed on the Slim client while in this plugin mode. Typically, this is labeled just as above, though it can be any name you wish. The lines subroutine, or other name if you have chosen one, returns a hash defining the display to be shown on the player. This can be updated at any time from any point in the plugin module by using the line:
$client->update();
Also included in the setMode subroutine are any commands or functions that must be run each time the plugin is called. This may be loading an array for a menu system, default settings, or running any number of other subroutines that are needed for the operation of the plugin.
You may also want to add extra modes under your plugin. There is a function call that tells the server to add your new mode with its setMode and getFunctions references.
Slim::Buttons::Common::addMode('newmodename', getNewModeFunctions(), \&::PluginName::setNewMode);
and then you can include the following in your plugin to hook-in to your new mode:
sub setNewMode { my $client = shift; $client->lines(\&newModeLines); } sub getNewModeFunctions { return \%newModeFunctions; }
The server handles all commands from the remote control for each mode by creating a hash table of functions, each button having a subroutine associated with it. The subroutine getFunctions() returns a reference to this hash, and can be any label you wish, although typically %functions is used. The call to point to this list is shown above in example 1. The function list, should look something like this example taken from rescan.pm, which is included with the server:
Example 2
my %functions = ( 'up' => sub { my $client = shift; $client->bumpUp(); }, 'down' => sub { my $client = shift; $client->bumpDown(); }, 'left' => sub { my $client = shift; Slim::Buttons::Common::popModeRight($client); }, 'right' => sub { my $client = shift; $client->bumpRight(); }, 'play' => sub { my $client = shift; my @pargs=('rescan'); Slim::Control::Request::executeRequest($client, \@pargs); $client->showBriefly( { $client->string('PLUGIN_RESCAN_MUSIC_LIBRARY'), $client->string('PLUGIN_RESCAN_RESCANNING'), }); } );
Each remote button (eg. 'play') points to a subroutine to be performed each time that button is pressed. In the case above, pressing play sets up local variables, starts a rescan of the entire library, then shows two lines on the display for a short time to tell the user that the rescan has been started. The line "my $client = shift;" is very important here to keep track of the player status, and to pass on in server function calls such as:
$client->showBriefly();
Examples of remote control functions include: 'up','down','play,'add' (REC button),'left','right','numberScroll' and 'stop' The full button to function map is found in the Default.map file, which is in the IR directory under the Lyrion Music Server directory.
By default, plugins will appear in the Plugins menu on the player. However, some plugins may be similar to others, and could be better organised into submenus. The addMenu function allows a plugin to choose its submenu location:
The simple line subroutine, below, is taken from Picks.pm.
This will make the plugin appear in a submenu with the string token RADIO. This submenu string can then be localized by the server to the chosen language. Each submenu can, as an option, be added to the top level menus for any player via the player settings.Example 3
sub addMenu { return "RADIO"; };
The lines subroutine returns the text that the Slim Client will display while using your plugin. The setMode() function creates the reference for the lines subroutine, and your lines subroutine name must match that. Each mode may have its own lines subroutine, and each is named after the reference created in each setMode. The input for this function is the current client information, and the return is a hash defining the display. This is defined in detail in Display API.
The lines are sent to the display at any time using the command:
$client->update();
The simple line subroutine, below, is taken from Rescan.pm.
Example 4
sub lines { my $client = shift; return { 'line1' => $client->string('PLUGIN_RESCAN_MUSIC_LIBRARY'), 'line2' => $client->string('PLUGIN_RESCAN_PRESS_PLAY'), } }
The plugin API also allows you to add in your own localization. The format of the strings list follows the same format as the strings.txt file used by the server for localization. The function strings() can be used within the plugin to extract a strings for the user's specified language. Defining the strings is done as follows:
and to use your strings in your module, you make the call to strings in any place where you would use a text string. For example:Example 6
sub strings { return ' PLUGIN_STRING_ONE <tab> EN <tab> English version of line one <tab> FR <tab> Version française de la ligne une '};
$client->string('MY_STRING');where your strings function contains:
sub strings { return ' MY_STRING <tab> EN <tab> Useful text '};
One special note, the format of the strings list is very strict. The whitespace must be a tab, not spaces, which is why the tabs are shown above.
Lyrion Music Server Plugins can expose their own web interface, accessible through the main server's web pages. To do this, include a function called "webPages" which returns a list of two values: a hash reference to handler functions for the plugin's pages, and a string representing the path of the plugin's index page.
The handler function hash contains keys that represent regular expression values. These regular expressions match URL paths that will be handled by the corresponding page handler. For example, the regular expression "index\.(?:htm|xml)" will match the URL paths "index.html", "index.htm" or "index.xml".
The index page string represents the URL path for a link that will be included in the "Plugins" section of the server index page. When exposed through the server's web interface, all plugin web pages are prepended with the URL path http://<host>/plugins/<pluginname>/. However, neither the regular expression keys nor the index URL path need to take this prefix into account.
Example 7
sub webPages { my %pages = ( "index\.(?:htm|xml)" => \&handleIndex, "enable\.(?:htm|xml)" => \&handleEnable, ); return (\%pages, "index.html"); }
Plugins also have the option of adding links to other sections of the server index page. Specifically, links can be added to the "browse", "search", "settings", "plugins" and "help" sections. Note that not all skins may support every section. Links can be added using the Slim::Web::Pages::addLinks() method. The method takes two parameters: the category to which links should be added, and a reference to a hash that maps link text to a URL path. In this case, the URL path must include the "plugins/<pluginname>/" prefix.
Slim::Web::Pages::addLinks("search", { $client->string('PLUGIN_MYPLUGIN_SEARCH_LINK') => "plugins/MyPlugin/search.html", );
sub screenSaver { my $client = shift; Slim::Buttons::Common::addSaver('SCREENSAVER.saverModeName', getScreensaverFunctions(), \&setScreensaverMode, \&leaveScreensaverMode, $client->string('PLUGIN_SCREENSAVER_NAME')); }
There are several other optional plugin functions that allow you to control how you plugin will integrate with the server:
Using the existing plugins as examples (one appears below) and this document as an explanation of what each section of the plugin can do, you should now be able to start working on a plugin of your own. Remember that this is only a framework to allow you to hook into the server. There are always many other ways to implement the features of a plugin. As long as your provide the lines from the examples above, the server will try to work with your Plugin. The rest can be just about anything you want, including using any of functions and subroutines within the server. Remember, there's more than one way to do it.
Happy Coding!
Plugins made for server version before 5.0 need to be updated using whack.pl, included in the tools directory of the CVS version of server 5.0 and higher. Syntax for this command is:
whack.pl myplugin.pm...
This will rewrite myplugin.pm (and any other specified files), leaving a copy of the script in myplugin.pm.old, to use the new module layout.
# Rescan.pm by Andrew Hedges (andrew@hedges.me.uk) October 2002 # # This code is derived from code with the following copyright message: # # Logitech Media Server Copyright 2001-2024 Logitech. # Lyrion Music Server Copyright 2024 Lyrion Community. # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # version 2. use strict; ########################################### ### Section 1. Change these as required ### ########################################### package Plugins::Rescan; use Slim::Player::Playlist; use Slim::Utils::Strings qw (string); sub getDisplayName { return 'PLUGIN_RESCAN_MUSIC_LIBRARY' }; sub strings { return ' PLUGIN_RESCAN_MUSIC_LIBRARY EN Rescan Music Library PLUGIN_RESCAN_RESCANNING EN Server now rescanning... PLUGIN_RESCAN_PRESS_PLAY EN Press PLAY to rescan your music folder '}; ################################################## ### Section 2. Your variables and code go here ### ################################################## sub setMode { my $client = shift; $client->lines(\&lines); } sub enabled { my $client = shift; return !Slim::Music::iTunes::useiTunesLibrary(); } my %functions = ( 'up' => sub { my $client = shift; $client->bumpUp(); }, 'down' => sub { my $client = shift; $client->bumpDown(); }, 'left' => sub { my $client = shift; Slim::Buttons::Common::popModeRight($client); }, 'right' => sub { my $client = shift; $client->bumpRight(); }, 'play' => sub { my $client = shift; my @pargs=('rescan'); Slim::Control::Request::executeRequest($client, \@pargs); $client->showBriefly( { 'line1' => $client->string('PLUGIN_RESCAN_MUSIC_LIBRARY'), 'line2' => $client->string('PLUGIN_RESCAN_RESCANNING'), }); } ); sub lines { my $client = shift; return { 'line1' => $client->string('PLUGIN_RESCAN_MUSIC_LIBRARY'), 'line2' => $client->string('PLUGIN_RESCAN_PRESS_PLAY'), }; } ################################################ ### End of Section 2. ### ################################################ sub getFunctions { return \%functions; } 1;[% PROCESS helpfooter.html %]