Plex Plug-in Framework
Documentation
Release 2.1.1
Plex, Inc.
May 25, 2011
CONTENTS
1 Introduction 1
2 How plug-ins work 3
2.1 Differences between plug-ins and regular Python programs . . . . . . . . . . . 3
3 Bundles 5
3.1 Configuration files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.2 Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4 Channel Plug-ins 11
4.1 Getting started with channel development . . . . . . . . . . . . . . . . . . . . 11
4.2 Site Configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.3 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5 Agent Plug-ins 33
5.1 Getting started with agent development . . . . . . . . . . . . . . . . . . . . . 33
5.2 Searching for results to provide matches for media . . . . . . . . . . . . . . . 35
5.3 Adding metadata to media . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.4 Metadata Model Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6 API Reference 47
6.1 Standard API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7 Site Configuration Reference 91
7.1 Tags (Alphabetical) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.2 XML DTD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
7.3 Keyboard Keycode Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
8 Indices and tables 107
Python Module Index 109
Index 111
i
ii
CHAPTER
ONE
INTRODUCTION
The Plex Plug-in Framework provides a simple yet powerful platform that enables developers
to easily create plug-ins for Plex Media Server. The framework consists of an API containing a
set of tightly integrated methods, and a small runtime environment that manages the execution
of a plug-in’s code.
The framework API builds upon a great foundation provided by the Python programming lan-
guage, and concentrates on making the areas of Python most frequently used when developing
plug-ins even easier to access. The API handles a lot of the boilerplate code internally, allow-
ing the developer to focus on functionality, and the runtime environment manages most of the
interaction between plug-ins and the media server.
Plug-ins created using the Plex Plug-in Framework are typically very small and lightweight,
and can achieve a great deal of functionality in hardly any time. You’ll be amazed by what you
can accomplish!
1
Plex Plug-in Framework Documentation, Release 2.1.1
2 Chapter 1. Introduction
CHAPTER
TWO
HOW PLUG-INS WORK
Each media server plug-in runs in its’ own process, in a separate Python instance. This en-
sures that plug-ins can’t interfere with each other, or with the operation of the server. Plug-ins
communicate with the media server over sockets and act like mini HTTP servers, receiving
standard HTTP requests and responding with XML-formatted data. This process is completely
transparent to the plug-in developer - the framework will handle receiving requests, routing
them to the correct place within a plug-in, and sending a response back to the server.
Plug-in’s don’t behave quite like normal Python programs. A plug-in isn’t a program in its’
own right - the framework’s runtime environment is the program, which dynamically loads the
plug-in code as part of the initialization routine. The runtime handles common tasks like run
loop management, request handling, logging and data storage, and calls specific functions in
the plug-in when necessary.
2.1 Differences between plug-ins and regular Python
programs
Because media server plug-ins are a little different to regular Python programs, they can behave
strangely in certain situations. There are a few key areas where regular Python rules do not
apply that you should be aware of.
2.1.1 Code structure
Because plug-ins are executed within the framework’s runtime environment, the code
structure is more strict than in a normal Python program. All of the plug-in’s code
needs to be contained within functions. Although it is generally safe to initialize sim-
ple variables with types like str (http://docs.python.org/library/functions.html#str),
int (http://docs.python.org/library/functions.html#int) or bool
(http://docs.python.org/library/functions.html#bool) outside a function, anything more
complicated (especially involving framework functions) may produce unexpected results or
fail completely. This is because the runtime environment needs to be fully initialized before
using any of the framework methods, and code in plug-ins that exists outside a function is
executed as soon as the plug-in is loaded. Any code you want to execute when the plug-in
3
Plex Plug-in Framework Documentation, Release 2.1.1
loads should be put inside the Start() (page 13) function. More information can be found
in the functions section.
2.1.2 The framework APIs
One of the biggest hurdles for existing Python developers to overcome is breaking old
habits when it comes to developing plug-ins. For instance, many developers may already
be familiar with using the urllib (http://docs.python.org/library/urllib.html#module-urllib)
and urllib2 (http://docs.python.org/library/urllib2.html#module-urllib2) modules to make
HTTP requests, but all of the code required to perform a request can be replaced by calling a
single method in the HTTP (page 69) API. This also has other benefits, like automatic cookie
handling and the option to cache the server’s response.
While creating the framework APIs, a great deal of care was taken to make plug-in development
as easy as possible. If you take the time to familiarize yourself with how the framework operates
and the features it provides, you’ll become much more productive when writing plug-ins.
2.1.3 Plug-in execution
Another side effect of the plug-in architecture is that plug-ins are incapable of running by
themselves. Because they rely heavily on the framework APIs and the runtime environment,
plug-ins must be started by the media server.
4 Chapter 2. How plug-ins work
CHAPTER
THREE
BUNDLES
Each plug-in’s code and support files are stored in a self-contained bundle. A bundle is a
specially organized directory with a .bundle extension. Mac OS X treats bundles as if they
were files, allowing you to manipulate them as such in Finder. To view the contents of a
bundle, right-click on it & select “Show Package Contents”. On Windows or Linux, bundles
appear as regular folders.
Here’s an example of the contents of a bundle:
The bundle directory itself contains only one item: the Contents directory. This directory
contains all files and other directories belonging to the plug-in. The Contents directory
should include the following items:
5
Plex Plug-in Framework Documentation, Release 2.1.1
3.1 Configuration files
The framework uses certain configuration files to provide information about the bundle, set up
defaults and alter how the runtime environment operates.
3.1.1 The Info.plist file
The Info.plist file contains information in the Apple property list format about the bun-
dle’s contents. This information is used by the framework when loading the plug-in.
Note: This file is required.
Here is an example Info.plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.plexapp.plugins.amt</string>
<key>PlexAudioCodec</key>
<array>
<string>AAC</string>
</array>
<key>PlexClientPlatforms</key>
<string>MacOSX,iOS,Android,LGTV</string>
<key>PlexFrameworkVersion</key>
<string>2</string>
<key>PlexMediaContainer</key>
<array>
<string>M4V</string>
<string>MOV</string>
</array>
<key>PlexVideoCodec</key>
<array>
<string>H.264</string>
</array>
<key>PlexURLServices</key>
<dict>
<key>Apple Movie Trailers</key>
<dict>
<key>URLPattern</key>
<string>http://.
*
apple\.com/.
*
</string>
<key>Identifier</key>
<string>com.plexapp.plugins.amt</string>
</dict>
</dict>
</dict>
6 Chapter 3. Bundles
Plex Plug-in Framework Documentation, Release 2.1.1
</plist>
The following properties may be defined:
CFBundleIdentifier
A string property containing a unique identifier for the plug-in. This should be in the
reverse DNS format (e.g. com.mysite.myplugin).
PlexFrameworkVersion
A string property specifying the bundle’s target framework version. This should be set to
2 to target the current framework version.
PlexPluginClass
A string plug-in identifying the class of the bundle. This should be set to either Content
or Agent.
PlexClientPlatforms
A string specifying a comma-separated list of client platforms supported by the bundle’s
contents. The list should contain constants from ClientPlatform (page 88).
PlexURLServices
PlexSearchServices
PlexRelatedContentServices
These properties describe the services available within the bundle. More information
about these keys can be found here (page 25).
Note: These properties are optional.
PlexMediaContainer
An array of strings identifying the containers returned by the plug-in. The array should
contain constants from Container (page 89).
Note: This property is optional.
PlexAudioCodec
An array of strings identifying the containers returned by the plug-in. The array should
contain constants from AudioCodec (page 89).
Note: This property is optional.
PlexVideoCodec
An array of strings identifying the containers returned by the plug-in. The array should
contain constants from VideoCodec (page 89).
Note: This property is optional.
3.1. Configuration files 7
Plex Plug-in Framework Documentation, Release 2.1.1
3.1.2 The DefaultPrefs.json file
DefaultPrefs.json is a JSON-formatted file containing a set of preferences used by the
plug-in, and the default values to use if the user hasn’t specified their own.
More information about the preferences file format can be found here (page 85).
3.2 Directories
The remaining files inside the bundle are separated into specific directories, depending on their
purpose.
3.2.1 The Code directory
The Code directory contains all of the plug-in’s source code files. Third-party code should not
be included here, only files created by the developer.
The __init__.py file
The __init__.py file is the main source code file of the plug-in. It is responsible for any
initialization required, and loading any extra source code files.
See Also:
The Start() (page 13) function The predefined framework function which can be used to
initialize your plugin.
3.2.2 The Services directory
The Services directory contains source code files for the plug-in’s services.
More information about services can be found here (page 25).
3.2.3 The Resources directory
The Resources directory contains any resource files required by the plug-in, like icons or
background images.
Information about loading these resource files in plug-in code is provided in the Resource
(page 66) API reference.
8 Chapter 3. Bundles
Plex Plug-in Framework Documentation, Release 2.1.1
3.2.4 The Strings directory
The Strings directory contains JSON-formatted text files for each language the plug-in sup-
ports. String files should have a .json extension and be named according to the ISO speci-
fication for language codes (e.g. en.json). The name may also include an optional country
code (e.g. en-us.json).
More information about string files can be found in the Locale (page 79) API reference.
3.2. Directories 9
Plex Plug-in Framework Documentation, Release 2.1.1
10 Chapter 3. Bundles
CHAPTER
FOUR
CHANNEL PLUG-INS
Channel plug-ins are the plug-ins users interact with most frequently. They integrate into the
Plex client user interface, providing a new branch of media for the user to explore. Developers
can easily create plug-ins that provide a hierarchy of media for the user to explore.
4.1 Getting started with channel development
4.1.1 What are channels?
Channels are plug-ins that provide playable content to clients via the media server. They can
extend the server’s media tree to provide access to almost any type of content available online,
and present a rich, browsable hierarchy to the user.
4.1.2 Understanding contexts
Before starting to write code, the developer should understand the concept of contexts within
the runtime environment. A context is an encapsulation of certain state information. When
your plug-in first starts, a single context is created - this is referred to as the global context.
As a plug-in receives requests, the framework creates a new context for each one - these are
referred to as request contexts.
Many of the framework’s APIs are context aware, meaning that they are able to act on infor-
mation stored in the current context. What this means for the developer is that they need to do
much less work to effectively support multiple parallel requests. The framework will ensure
that API calls from plug-in code return values based on the correct context, affecting all manner
of things from string localization, to user preferences, to supported object types.
There is only one caveat to this method - global objects cannot be modified from request con-
texts. Since plug-ins can potentially serve thousands of simultaneous requests, a global object
could be modified by one request while it was still in use by another. Rather than use global ob-
jects, the developer should attempt to maintain state by passing arguments between functions,
or using one of the data storage APIs provided by the framework.
11
Plex Plug-in Framework Documentation, Release 2.1.1
4.1.3 Configuring the Info.plist file
To indicate that the bundle contains a channel plug-in, the PlexPluginClass key should be set
to Content in the Info.plist file.
4.1.4 Adding a prefix
Channel plug-ins add themselves to the user interface by first defining a prefix. Once a prefix
has been registered, the plug-in effectively ‘owns’ all requests received by the media server
beginning with that prefix.
To register a prefix, the developer must define a function to be the main prefix handler using
the @handler (page 47) decorator:
@handler(’/video/example’, ’Example’)
def Main():
pass
Any incoming requests with the /video/example prefix will now be routed to this plug-in,
and requests matching the path exactly will be directed to this function, and the channel will
appear as a new icon in each supported client’s Channels menu.
4.1.5 Creating a menu hierarchy
To create the beginning of a browsable tree of metadata items, the developer needs to create
an ObjectContainer (page 52), populate it with objects and return it to the client. The
objects in this container can point to other functions within the plug-in, allowing the developer
to define a rich navigation hierarchy.
The simplest and most common use of function callbacks is to connect a DirectoryObject
(page 62) to a function that will generate another ObjectContainer (page 52) (providing
a new level of navigation) using the Callback() (page 48) function:
def Main():
oc = ObjectContainer(
objects = [
DirectoryObject(
key = Callback(SecondMenu),
title = "Example Directory"
)
]
)
return oc
def SecondMenu():
oc = ObjectContainer(
...
)
return oc
12 Chapter 4. Channel Plug-ins
Plex Plug-in Framework Documentation, Release 2.1.1
The Callback() (page 48) function can also be used to return image data for thumbnails or
background art, or provide data for a media object (usually by redirecting to the real source).
4.1.6 Predefined functions
The framework reserves a few predefined function names. The developer can implement these
functions if they wish to. They will be called when specific events occur.
Start()
This function is called when the plug-in first starts. It can be used to perform extra
initialisation tasks such as configuring the environment and setting default attributes.
ValidatePrefs()
This function is called when the user modifies their preferences. The developer can check
the newly provided values to ensure they are correct (e.g. attempting a login to validate a
username and password), and optionally return a MessageContainer to display any
error information to the user.
SetRating(key, rating)
This function is called when the user sets the rating of a metadata item returned by the
plug-in. The key argument will be equal to the value of the item’s rating_key at-
tribute.
4.2 Site Configurations
Note: Playback of Flash or Silverlight content using site configurations is currently only
supported on Mac OS X.
4.2.1 Overview
A Site Configuration file allows a Flash or Silverlight player to be played within a web browser.
Pretend the you could fire up a web browser, go to your favorite video site and draw a rectangle
around just the video player. The video player would then be zoomed in to fill the screen and
all of other web page junk is just thrown away so that you can watch the video without any
distractions.
Site Configurations are complements to WebVideoItem() in the plugin framework. They
act as handlers to the urls that the WebVideoItem() passes out.
When you should NOT create a Site Config
If you have access to the video stream directly, then you should not use a site configuration file.
4.2. Site Configurations 13
Plex Plug-in Framework Documentation, Release 2.1.1
You will have to do some sleuthing to see if you can get access to the .flv, .mp4 or RTMP
streams directly. You can use tools like the Net inspector in Firebug or you can sometimes find
the stream URL passed in as a parameter to the player if you view the HTML source.
Some formats will just not work. RTMPE (note the E) or other formats which have DRM will
have to be played with the flash/silverlight player and warrant the creation of a Site Configura-
tion file
Where to put them
Site configurations can go one of two places.
1. (recommended) Inside of your plugin bundle file
MyPlugin.bundle/Contents/Site Configurations/yourfile.xml
2. ~/Library/Application Support/Plex Media Server/Site
Configurations/yourfile.xml
4.2.2 Gathering Player Information
When creating site configs, often you will need to have access to information such as:
1. Url’s of the webpage / player
2. Height and Width of the video player
3. X/Y coordinates of buttons on the player
4. X/Y coordinates of other elements / colors on the screen
You’ll use this information later on to do things like “click on a button” or “find out what color
the pixel is here”
This information can be a real pain to get without some tools and techniques to help you out.
Here are just a few.
URLs
You will need two urls saved away somewhere for future reference. Later on, you will use these
URLs in order to create two regular expressions for the <site> (page 100) tag
The first URL you will need is the URL of the page which contains the Flash or Silverlight
player. You can simply copy and paste the URL from your browser URL bar.
The other URL you will need is the URL of the Flash or Silverlight player. You can use the
“View Source” function of your web browser and then search for the URL that is used in the
<embed> tag for the plugin.
Now that you have these URLs save them away in a text file for later use.
14 Chapter 4. Channel Plug-ins
Plex Plug-in Framework Documentation, Release 2.1.1
Player Size and Coordinates
Firebug
Install Firebug (http://getfirebug.com/) for Firefox (http://getfirefox.com) and then simply visit
the webpage containing the video player.
From here, (1) open Firebug and (2) click on the “Inspect” icon and then click on the (3) flash
player in the page.
Information about this element list CSS style information now shows on the right hand side.
Click on the (4) “Layout” tab over on the right.
You should now see (5) height and width information for the selected element on the page.
Free Ruler
Another excellent tool to use (OSX) is called Free Ruler
(http://www.pascal.com/software/freeruler/).
Download and install this program and you will be able to overlay the horizontal and vertical
rulers over the top/left edge of the flash player. With FreeRuler in the foreground, you’ll be
able to hit Command-C to copy the x/y coordinates of the current mouse position so that you
can use this information later on with tags such as <click> (page 93) and <move> (page 96).
Colors
You can use the DigitlColor Meter tool which comes with OSX to find out the hex color value
of any pixel on the screen.
4.2. Site Configurations 15
Plex Plug-in Framework Documentation, Release 2.1.1
4.2.3 The XML File
The <site> Element
Site configuration files all start out with the <site> tag along with the proper <xml? ..
definition. A bare bones example looks like so:
<site site="http://www.southparkstudios.com/episodes"
plugin="http://media.mtvnservices.com/mgid.
*
southparkstudios.com"
initialState="playing"
version="1.0">
<!-- ... -->
</site>
The site and plugin attributes are actually regular expressions which match URLs
The site regular expression must match the URL for the webpage which contains the flash
or silverlight plugin. This regular expression is scanned whenever a WebVideoItem url is
played.
If another site configuration file has a url which also matches the url, then the regular expression
which is the most specific (longer) wins and the site configuration file containing that regular
expression is run.
The plugin regular expression must match the url for the embedded player. This URL is
usually found in the <embed> tag of the web page.
The initialState attribute tells the player to begin the the <state> (page 100) tag with the
corresponding name attribute. More on States and Events (page 18)
Seekbars
A seekbar is a representation of your progress through a video. It shows you information such
as:
1. How far through the video you are
2. Control for skip forward / skip backward
Some seekbars are graphical in nature only. For these, use the ‘simple’ or ‘thumb’ value for the
type attribute on the <seekbar> (page 99) tag.
Some Flash/Silverlight players expose hooks in their players which allow javascript to actually
control and retrieve information from the player. This is an advanced topic and you would use
‘javascript’ for the type attribute on the <seekbar> (page 99) tag.
16 Chapter 4. Channel Plug-ins