Just a little bit further

Developing a Plex video plugin

| development HTPC Plex plugin python

Within my HTPC setup I’ve put in a few useful plugins.  Particularly the official ABC and unofficial SBS plugin.  The Australian commercial channels are now on the scene with on-demand TV available to be streamed online.

Since no one else has yet – I set about taking the Channel 10 website with it’s flash video content and surfacing it in a Plex video plugin.  When I got stuck – someone on the forums indicated that they’d setup the menu system for a Channel 9 but were outside of Oz so couldn’t stream the video content.   From novice (new to Python and Plex plugin development) to having 2 functioning, useful, plugins was pretty easy.  Hopefully this will help others who want to give it a try.

9 MSN and SC 10 plugins

9 MSN and SC 10 plugins

Tools Required

  • Firefox + plugins
    • HttpFox – to sniff traffic as you browse a site
    • Firebug – to inspect elements in a web page
    • XPath Checker – to interactively run XPath queries against a URL (html/xml)
  • Python IDE (Komodo worked well)
  • Silverlight browser plugins (to play silverlight media)
  • Flash v10.0 (NB. v10.1 has an issue causing only audio to play – downgrade if necessary)

Of course – Plex9 is also essential.

The Basics

Before you start – you should check nobody else is already developing your plugin, and let others know you are starting a new plugin.  Add an entry to the plugins in progress page.

Plex Plugins are installed either from within Plex (app store) or can simply be copied into:

~/Library/Application\ Support/Plex\ Media\ Server/Plug-ins

Take a like plugin (ie. a video plugin) and copy to a working directory.  The structure is shown below.

  • Plugin folder structureMain code for a plugin is in __init__.py
  • Site Configuration is only required when playing inline media in a webpage
  • Resources hold background images and the plugin icon

If you need to peek inside the bundle – either right click in Finder and select ‘Show Package Contents’ ,  or change the plugins name to remove the .bundle and turn it into a standard folder (accept the friendly OS X warning).

In general terms – the Plex architecture makes it straight forward to setup a nice plugin.  In the __init__.py you will need to

  1. Setup the menu structure and populate menu items based metadata you can scrape from the website (eg. categories, most popular, recently added)
  2. Scrape information from the website to populate the title, thumbnails, summary for a series/episode.
  3. Ensure you have a valid URL so the requested media will play when selected.

Sounds simple – but it did take a number of iterations and a few scratches of the head to work through.

How to Go About it

Any new plugins copied into the plug-ins directory are automatically picked up and deployed by the Plex Media Server.  You can view the logs to see what is going on

~/Library/Logs/Plex.log
<span style="color: #000080;">~/Library/Logs/Plex\ Media\ Server.log</span><br />

But it is much simpler to run Plex Media Server in debug mode from within Terminal:

Thumper:~ Adam$  /Users/Adam/Library/Application\ Support/Plex/Plex\ Media\ Server.app/Contents/MacOS/Plex\ Media\ Server woof<br /> Enabled debugging mode.<br /> 2010-10-03 11:54:53.289 Plex Media Server[8594:3837] Initializing Cocoa updater

As you develop you will want to include calls to the Plex Log function to assist in debugging, these messages will be written to both the console and Plex Media Server logs.

Log("Adding seriesID = " + seriesInfo['id'])

I’d recommend focusing initially on getting your menu structure and items working.  This can be done by using XPath queries against the source from the website page(s).  In the case of the SC10 plugin, by sniffing traffic I found the TV shows online were both displayed in elements in the HTML page, but also catalogued in XML files.  Learning as I went I used the XML files, which could also be easily parsed using XPath queries.  The downside to this is the web page(s) have some extra images that would have been useful for thumbnails.  I’ll revisit this for v0.2.

View XPath

Once you’ve got the appropriate URL (with categories, or all shows listed) point FireFox there and right click (on the element of interest) and select ‘ViewXPath’

Interactively run XPath queries to scrape appropriate categories

The XPath Checker utility then lets you interactively query the page until you get valid entries for your menu.  Pulling this together in Python is then pretty straight forward:

xml = XML.ElementFromURL(URL)

categories = {}<br /> for category in xml.xpath('XPATHEXPRESSION'):<br />

id = category.find('id').text<br /> name = category.find('title').text<br /> categories[id] = name<br /> return categories

From these categories we can setup the Main Video Menu – ie. Top level menu

def VideoMainMenu():<br /> dir = MediaContainer(viewGroup="InfoList")<br /> for name, id in categories:<br /> dir.Append(Function(DirectoryItem(CategoryMenu, name), category=id))<br /> return dir

Note we’ve added to this Main Menu – the DirectoryItem CategoryMenu this function will be called upon selecting one of our items in the Main Menu.  This means you can construct your own menu structure, and handle various menu selections differently if required.  At some point you’ll have traversed your menu structure down to a video item you want to play  – at this point in your Python code you’ll be adding a Media Item rather than a DirectoryItem.

Plex has a number of media items – the one you use will depend on the type of media you are playing

  • WebVideoItem
    Use this when you are playing a flash/silverlight video embedded within a webpage.
  • VideoItem
    Use this item if you have an mpeg or flv file you want to play directly
  • RTMPVideoItem
    Use this if you have streaming media to play
  • WindowsMediaVideoItem
    Stream windows media

In my case – SC10 used flash video and Channel9 used silverlight.  I couldn’t get a direct URL to a media file – so had to play the content inline within the web page.

dir.Append(WebVideoItem(episode['playerUrl'], title=episode['title'], subtitle="",<br /> summary=description, thumb=episode['thumb'], duration=""))

Because this was embedded content – the plugin needs a Site Configuration file for it to understand the dimensions of the content, where the seekbar is (so you can pause/fast forward/view progress), etc.  This took me a while to get right – but in the end it was simple:

<site site="http://ten.com.au/.*"<br /> plugin="http://apps.v2.movideo.com/player/flash/movideo_player.swf"<br /> initialState="playing"<br /> version="1.0">

<crop x="0" y="0" width="512" height="288" />

Look at the reference document for creating a site config.  The downside to both Ch9 and SC10 is they hide the seek bar – so it is not possible to fast forward, pause or track progress through a show.

.. and viola two new (currently unsupported) plugins developed and published.  Downloadable here–> Ch9 and here –>SC10

SC 10 plugin - loading streaming content

SC 10 plugin - loading streaming content

SC10 playing content

SC10 playing content

References