TunerFreeMCE Plugin Creation
It is possible to produce a plugin for TunerFreeMCE to bring in content from any source. This allows new channels to be added, and allows programs to be cached and viewed for those channels. This document describes how to produce thos plugins, using a Sling.com plugin as an example
1. Create plugin directory
Each plugin will be delivered in it’s own directory, so create an appropriately named directory. In that directory you can place all of the files required for your plugin.
2. Create plugin definition file
The primary file that defines your plugin is an xml definition file called config.xml. Create that file in your plugin directory. The contents of the file are;
- Definition of the plugin
- A list of channels provided by the plugin
- Details of how to read the list of programs
- Details of how to play the program
3. Plugin definition
The root element of the plugin document is called TunerFreePlugin. It has the following attributes;
- name. This is the name of the plugin used to display to the user which plugins are installed
- provider. This is a code used to identify the provider of the content. It should only contain a-Z, 0-9 and _.
- version. This us a number that will be used for checking for new versions
- country, This should be an ISO country code. Used for VPD switching.
- programType. Can be PLAYER, FLASH, WMV or WM. See section 7 for details.
- parserProgram. Optional. This is the name of a library that you can produce to do the parsing if you need to override the standard behaviour. See section 9 for details.
e.g.
<TunerFreePlugin name=”Sling” version=”1.0″ country=”US” provider=”SLING” programType=”FLASH”>
</TunerFreePlugin>
4. Plugin channels
A child of the root element is channels. This element contains the definition of all of your channels in the channel element. The channel element has the following attributes;
- name. This is the display name of the channel
- code. This is the internal code used to identify the channel. It should start with the provider name, and only contain a-Z, 0-9 and _.
- logo. This is a url to the logo of the TV channel that will be used on the list of channels. This can be an http://, file:// or resx:// url, or can be a filename in the plugin directory. This will be displayed at a size 244×154
- smallLogo. Optional. This is a url to the logo of the TV channel that will be displayed next to the program name. This can be an http://, file:// or resx:// url, or can be a filename in the plugin directory. This will be displayed at a size 20×20. If this is not specified, the logo url will be used.
- background. Optional. This is a url to the background that will be shown when browsing this channel. This can be an http://, file:// or resx:// url, or can be a filename in the plugin directory. If this is not specified, the default TunerFree logo will be used.
- feed. This is the url which the system should go to to get a list of programs for the channel.
e.g.
<TunerFreePlugin name=”Sling” provider=”SLING”>
<channels>
<channel name=”CBS” code=”sling_cbs” smallLogo=”http://betaimg1.sling.com/sli/images/dyn/network/15_clip_logo/rs/120/90/15_clip_logo.jpg?v=6″ logo=”http://betaimg1.sling.com/sli/images/dyn/network/15_clip_logo/rs/120/90/15_clip_logo.jpg?v=6″ feed=”http://beta.sling.com/network/15/CBS/videos.rss”/>
<channel name=”NBC” code=”sling_nbc” logo=”http://betaimg0.sling.com/sli/images/dyn/network/74_clip_logo/rs/120/90/74_clip_logo.jpg?v=3″ feed=”http://beta.sling.com/network/74/NBC/videos.rss”/>
<channel name=”Fox” code=”sling_fox” logo=”http://betaimg1.sling.com/sli/images/dyn/network/41_clip_logo/rs/120/90/41_clip_logo.jpg?v=4″ feed=”http://beta.sling.com/network/41/Fox/videos.rss”/>
<channel name=”Marvel” code=”sling_marvel” logo=”http://betaimg3.sling.com/sli/images/dyn/network/302_clip_logo/rs/120/90/302_clip_logo.jpg?v=2″ feed=”http://beta.sling.com/network/302/Marvel/videos.rss” background=”sling-background.jpg”/>
</channels>
</TunerFreePlugin>
5. Feed tags
For feeds that are well formatted xml (e.g. rss, atom), the contents of the feed are read by specifying the tag names for each attribute that we are interested in inside the parser section of the definition. Each of those tag names is specified in one of the following definition tags;
If the value you need to get from the xml is the tag value (e.g. <id>123</id>, simply specify the tag name in the definition (e.g. <programIdTag>id</programIdTag>).
If the value you need is in an attribute of the tag (e.g. <id val=”123″/>), specify the attribute name in the “attr” attribute of the definition (e.g. <programIdTag attr=”val”>id</programIdTag>).
If there are multiple nodes with the same name in the feed, you will need to pick one of them e.g.
<link rel=”self” href=”http://dj.rte.ie/vodfeeds/feedgenerator/videos/show/?id=1050152″ />
<link rel=”alternate” href=”http://www.rte.ie/player/#v=1050152″ />
you tell the system which one to pick by using the findAttr and findAttrVal attributes. findAttr is the name of the attribute to look for, and findAttrVal is the value in that attribute that needs to be matched (e.g. <linkTag attr=”href” findAttr=”rel” findAttrVal=”alternate”>link</linkTag>)
To read the date format, also specify a date mask in C# .NET format, e.g. <dateFormat>ddd, dd MMM yyyy HH:mm:ss</dateFormat>. Your date string may contain information that you don’t want to parse at the end, e.g a timezone. To ignore that, simply don’t specify the whole date format string – the date will be truncated to the length of the format string before parsing.
an example is;
<TunerFreePlugin name=”Sling” version=”1.0″ country=”US” provider=”SLING” programType=”FLASH”>
<channels>
<channel name=”CBS” code=”sling_cbs” smallLogo=”http://betaimg1.sling.com/sli/images/dyn/network/15_clip_logo/rs/120/90/15_clip_logo.jpg?v=6″ logo=”http://betaimg1.sling.com/sli/images/dyn/network/15_clip_logo/rs/120/90/15_clip_logo.jpg?v=6″ feed=”http://beta.sling.com/network/15/CBS/videos.rss”/>
<channel name=”NBC” code=”sling_nbc” logo=”http://betaimg0.sling.com/sli/images/dyn/network/74_clip_logo/rs/120/90/74_clip_logo.jpg?v=3″ feed=”http://beta.sling.com/network/74/NBC/videos.rss”/>
<channel name=”Fox” code=”sling_fox” logo=”http://betaimg1.sling.com/sli/images/dyn/network/41_clip_logo/rs/120/90/41_clip_logo.jpg?v=4″ feed=”http://beta.sling.com/network/41/Fox/videos.rss”/>
<channel name=”Marvel” code=”sling_marvel” logo=”http://betaimg3.sling.com/sli/images/dyn/network/302_clip_logo/rs/120/90/302_clip_logo.jpg?v=2″ feed=”http://beta.sling.com/network/302/Marvel/videos.rss” background=”sling-background.jpg”/>
</channels>
<parser>
<programTag>item</programTag>
<episodeTag>title</episodeTag>
<dateTag>pubDate</dateTag>
<dateFormat>ddd, dd MMM yyyy HH:mm:ss</dateFormat>
<linkTag attr=”href” findAttr=”rel” findAttrVal=”alternate”>link</linkTag>
<programIdTag>media:player</programIdTag>
<programTitleTag attr=”url”>media:title</programTitleTag>
<programDescriptionTag>media:description</programDescriptionTag>
<programIconTag attr=”url”>media:thumbnail</programIconTag>
</parser>
</TunerFreePlugin>
6. Feed parsing
For content that cannot easily be defined as an xml tag, you can define a start and end string to search for. The value will be taken as the content between the start and end tag, e.g. if we wanted to get the program name from the text “programName=Doctor Who,episodeName=..” then we would define
<programStart>programName=</programStart>
<programEnd>,</programEnd>
If you need to search for multiple strings to find the exact position, i.e. find the first occurrence of string 2 after string 1. For example, when searching in the following “tag=programName,value=DoctorWho,tag=episodeName,value=…” you could specify the following;
<programStart>tag=programName</programStart>
<programStart2>value=</programStart2>
<programEnd>,</programEnd>
The tags that you can use for this are;
programStart
titleStart
episodeStart
dateStart
dateFormat
linkStart
programTitleStart
programDescriptionStart
programIconStart
programIdStart
programEnd
titleEnd
episodeEnd
dateEnd
dateFormat
linkEnd
programTitleEnd
programDescriptionEnd
programIconEnd
programIdEnd
programStart2
titleStart2
episodeStart2
dateStart2
dateFormat
linkStart2
programTitleStart2
programDescriptionStart2
programIconStart2
programIdStart2
programEnd2
titleEnd2
episodeEnd2
dateEnd2
dateFormat
linkEnd2
programTitleEnd2
programDescriptionEnd2
programIconEnd2
programIdEnd2
7. Displaying the video
7.1 WMV and WMA
WMV and WMA content can be displayed directly within media center, so no further options are required for this.
7.2 Web based video
For web based video (flash and silverlight), there are two options for displaying the video. Option 1 is a new method for displaying the video in a standalone player, and is the suggested technique. Option 2 is an older method, and is included here for reference. Option 2 works fine for most web pages, but not those which cannot be displayed in an iframe, whereas Option 1 should work for everything
Option 1 – PLAYER
Option 1 will display the web page containing the video in a standalone application. The web page is manipulated to make the video full screen, but you must provide information about the web page to enable it to do that. When you set the programType to PLAYER, TunerFreeMCE will look for another file in your plugin folder called player_config.xml. This file contains some parameters required to manipulate the video. They are;
- playerWidth, playerHeight – the original width and height of the component that plays the video
- play00X, play00Y – the first points to click when first playing the video (optional)
- play0X, play0Y – the second points to click when first playing the video (optional)
- play1X, play1Y, – the third points to click when first playing the video. (optional)
- FSX, FSY – the click points to send the video full screen (optional – only use if the video cannot be scaled in css)
- playFSX, playFSY – the play/pause location while playing (optional)
- play00Delay – the time in seconds to wait before clicking play00 (optional – defaults to 5s)
- playDelay – the time in seconds to wait before clicking play0 (optional – defaults to 5s)
- zoomedId – the css tag to identify the components to be scaled to full screen (optional)
- movedId – the css tag to identify the components to be moved to the top left of the screen (optional)
- widenedId – the css tag to identify the components to be widened to the width of the screen (optional)
- backgroundId – the css tag of any background components to be blanked out (optional)
- hiddenId – the css tag of any items to be completely hidden (optional)
- extraCSS – any extra css required to make the screen display correctly (optional)
- deleteCache – true if the browser cache should be deleted before playing. A flash bug means that you should set this to True for most flash content, or it will only display once (optional)
Here’s a complete example from channel 5. Things to note are the player size is originally 640×360. The initial play button is at 318,169, and the play button while playing is at 218,354. The video is in a container with the id “video_box”, so that is zoomed and moved to the top left of the screen (the # at the start tells css to look for the item with the id that follows). The background is the “body” tag, and all “div”, “form” items are hidden as well as anything with a class of “ad” (the . before ad tells css to look for anything with a class of the name that follows). The cache is deleted before playing each time, and the extra css shown is added to move the video box to the top of the stack and to make sure it is visible
<?xml version="1.0" encoding="utf-8"?><PlayerConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <playerWidth>640</playerWidth> <playerHeight>360</playerHeight> <play0X>318</play0X> <play0Y>169</play0Y> <playFSX>218</playFSX> <playFSY>354</playFSY> <zoomedId>#video_box</zoomedId> <movedId>#video_box</movedId> <backgroundId>body</backgroundId> <hiddenId>.ad, div,form</hiddenId> <deleteCache>true</deleteCache> <extraCSS>#video_box{visibility: visible !important; z-index: 9999 !important;} </extraCSS> </PlayerConfig>
Here’s another complete example, this time for the 4OD plugin. Key points to note are the player size is 640×363. It needs to go full screen because it cannot scale the video in the window, to the FSX and FSY are specified. The player is still moved to the top left so that the clicks can occur, but the player is not zoomed (because that won’t work on this page).
<?xml version="1.0" encoding="utf-8"?><PlayerConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <playerWidth>640</playerWidth> <playerHeight>363</playerHeight> <FSX>625</FSX> <FSY>350</FSY> <playFSX>10</playFSX> <playFSY>360</playFSY> <movedId>#watch-player</movedId> <backgroundId>body</backgroundId> <hiddenId>div,span,img,p,iframe,table,a</hiddenId> <deleteCache>true</deleteCache> <extraCSS>#watch-player{visibility: visible !important;} #movie_player {z-index:9999 !important;}</extraCSS> </PlayerConfig>
In each case you may need to play with the settings to see what effect it has – different sites use different css and javascript tricks that mean there is no simple solution. An easy way to test though is to run the player standalone, passing in the parameters of what you want it to do. Here is an example;
"C:\Program Files (x86)\MillieSoft\TunerFreeMCE\TunerFreeMCEWebBrowser.exe " "http://www.bbc.co.uk/iplayer/tv/bbc_one_london/watchlive" "C:\Users\Bob\myplugin\player_config.xml" "Windows Media Center"
Option 2 – FLASH
Option 2 will display the video in a from in a web page within media center. The software will click on the appropriate points on the page to make the video play and go full screen. You need to tell the software where to click by giving the X and Y coordinates of a number of possible click points. You can include basic calculations using +, – and * and the constants H (screen height), H2 (half screen height), W (screen width) and W2 (half screen width). So, if the play button is 300 pixels to the left of the middle of the screen, specify it as “W2-300”. If a click is not applicable, specify the coordinates as 0,0.
The tags are;
- StartPlay. This is the location to be clicked to start playback first time.
- PlayNonFS. Play when not in full screen
- RevNonFS. Jump to start when not in full screen
- FS. Go to full screen
- PlayFS. Play when in full screen
- RevFS, Jump to start when in full screen
- HighRes. Toggle high resolution/low resolution
e.g.
<clicks>
<PlayNonFS x=”W2-490″ y=”540″/>
<StartPlay x=”0″ y=”0″/>
<RevNonFS x=”0″ y=”0″/>
<FS x=”W2+120″ y=”540″/>
<PlayFS x=”W2-306″ y=”H*0.96″/>
<RevFS x=”0″ y=”0″/>
<HighRes x=”0″ y=”0″/>
</clicks>
To calculate the click positions, I put media center in to full screen, navigate to a page and then take a screenshot. I then paste that in to Paint and use that to find the co-ordinates of points on the screen. When not in full screen, the Y positions tend to be a fixed number, and the X positions tend to be plus or minus a fixed amount from the middle of the screen. In full screen, the Y position tends to be a fraction of the screen height, and the X positions again tend to be plus or minus a fixed amount from the middle of the screen, but are sometimes a fraction of the screen width.
8. Authentication
Sometimes feeds need authentication, e.g. they need you to have logged in in your browser first. The cache builder can pick up a logged in session from your browser, but only if you add the site to your Trusted Sites in IE by going to the Security tab in Internet Options, clicking on Trusted Sites and then Sites, and adding the site to the list.
To allow authenticated access at the time of playing the video, be sure to add the site to the Sites list in the Privacy tab of the Internet Options.
9. Overriding the parser
If for some reason you absolutely cannot get the contents of the feed using the standard parser, you can write your own code to populate the program list. However, for most use cases this is unnecessary, and this step is optional.
To do this, you need to write a windows library. You can name the library whatever you like, but the namespace should be the same as the dll name, and the parser class should be the same as the namespace. For example, the Dave parser is in DaveParser.dll, which has a namespace of DaveParser, and a single class called DaveParser.
The parser class must be marked as Serializable, and must extend the class BaseParser which can be found in TunerFreeMCECore. There are two methods which you can override in BaseParser. Any which you do not override will defer to their default behaviour;
public virtual TVProgramList parsePrograms(TVProgramList programList,String currentDirectory)
this class is run when parsing. It takes an input of the current list of programs, and the current directory, and you need to return the new list of programs. You need to add or replace to the list. You must not create a new list, or previous programs will be lost. This is to allow you to add only missing programs if you need to. You can set values of added, removed and total for the number of items processed. This will be displayed in the parser window.
public virtual TVProgram populateProgramLink(TVProgram program)
this class is run when playing. It takes an input of a TVProgram, and should return the TVProgram with the details populated. You could set both the URL and the player type in this method if required.
Here’s a snippet of code to show how it’s done;
using System; using System.Net; using System.Text.RegularExpressions; using TunerFreeMCECore; namespace DaveParser { [Serializable] public class DaveParser :BaseParser { override public TVProgram populateProgramLink(TVProgram program) { program.programLink="http://"+; /* logic to get the program link */ return program; } override public TVProgramList parsePrograms(TVProgramList listIn, String currentDirectory) { removed=listIn.thePrograms.RemoveAll(delegate(TVProgram p1) { return p1.ProgramProvider.Equals("Dave"); }); added = 0; total = 0; pageCount = 0; using (WebClient w1 = new WebClient()) { w1.Encoding = System.Text.UTF8Encoding.UTF8; String showList = w1.DownloadString("http://video.uktv.co.uk/dave/programmes/a-z/see-all"); string[] showComponents = showList.Replace("<div class=\"promo\">", "¬class=\"promo\">").Split('¬'); foreach (string show in showComponents) { // do all of the processing to get the show details from the web page listIn.thePrograms.Add(new TVProgram(showTitle,showURL,episodeName,showImage,showSummary,"Dave","",DateTime.Now,"DAVE","file://"+currentDirectory+"\\dave.jpg",showURL,"PLAYER","Dave")); added = added + 1; total = total + 1; pageCount = pageCount + 1; } } } return listIn; } } }
10. Testing
To test your plugin, put it in the C:/ProgramData/MillieSoft/TunerFreeMCE/additional_plugins folder. This folder is used to pick up any plugins that are not installed directly via the plugins page in TunerFreeMCE.
After you have created your plugin, run TunerFreeMCECacheBuilder.exe to pick up your programs. If you run the code with a -debug flag, some additional information will be outputted that may help you if you are having problems.
11. Publishing
After you have created your plugin, email me at martin@milliesoft.co.uk to get your plugin added to the list of plugins shown within TunerFreeMCE. If your plugin includes a dll parser, please also include the source code for review. We need to do that to avoid malicious code.