Here is the code for my addon. A few things to note:
1) It requires having the Sage webserver and API installed as this creates the xml files with metadata
2) Since I've only been using this myself I haven't setup a proper "Settings" file yet, so you need to put your Sage server URL and port in the code (only one time at the top, and it reads it everywhere else needed), with username and password
3) In the "categories" function it calls the addDir function twice, once for all shows and once for The Daily Show. I have those as examples, you could just use the all shows to list everything, or create as many other folders as you'd like by copying the Daily Show line. You can do this with a specific show, category, or any search you can perform in the SageTV web interface. You just run the search, and then click the XML link to get the URL for the XML file. So you can have folders for a show, for the 10 most recent recordings, anything you want really.
4) One other edit you need: in the last line of the Videolinks function change the paths to whatever suits your system. This just changes the local paths in the SageTV XML to smb paths for XBMC. If you're running SageTV on the same machine it isn't needed.
I have a bunch of comments on how things are working since I was learning Python on the fly while writing this, that should help answer any questions, but let me know if something's not working and I can try to help.
ETA: To "install" this just create a folder called SageTV in your addons directory, and copy the code below into a file named Default.py and the xml file into a file called addon.xml
Code:
import urllib,urllib2,re
import xbmc,xbmcplugin,xbmcgui
from xml.dom.minidom import parse
def CATEGORIES():
strUrl = 'http://user:password@serverIPaddress:serverport'
addDir('All Shows', strUrl + '/sage/Recordings?xml=yes',2,'')
addDir('The Daily Show',strUrl + '/sage/Search?searchType=TVFiles&SearchString=Daily%20show&DVD=on&sort2=airdate_asc&TimeRange=0&pagelen=100&sort1=title_asc&filename=&Video=on&search_fields=title&xml=yes',2,'')
def VIDEOLINKS(url,name):
#Videolinks gets called immediately after adddir, so the timeline is categories, adddir, and then videolinks
#Videolinks then calls addlink in a loop
#print 'this is when videolinks gets called'
# print url
# print name
req = urllib.urlopen(url)
content = parse(req)
# Writing some code here to test xml parsing
for showlist in content.getElementsByTagName('show'):
strTitle = ''
strEpisode = ''
strDescription = ''
strGenre = ''
strAirdate = ''
#print 'showlist: '+showlist[0]
for shownode in showlist.childNodes:
# Get the title of the show
if shownode.nodeName == 'title':
strTitle = shownode.toxml()
strTitle = strTitle.replace('<title>','')
strTitle = strTitle.replace('</title>','')
strTitle = strTitle.replace('&','&')
#print 'showtitle: '+strTitle
# Get the episode name
if shownode.nodeName == 'episode':
strEpisode = shownode.toxml()
strEpisode = strEpisode.replace('<episode>','')
strEpisode = strEpisode.replace('</episode>','')
strEpisode = strEpisode.replace('&','&')
#print 'episode: '+strEpisode
# Get the show description
if shownode.nodeName == 'description':
strDescription = shownode.toxml()
strDescription = strDescription.replace('<description>','')
strDescription = strDescription.replace('</description>','')
strDescription = strDescription.replace('&','&')
#print 'description: '+strDescription
# Get the category to use for genre
if shownode.nodeName == 'category':
strGenre = shownode.toxml()
strGenre = strGenre.replace('<category>','')
strGenre = strGenre.replace('</category>','')
strGenre = strGenre.replace('&','&')
#print 'Genre: '+strGenre
# Get the airdate to use for Aired
if shownode.nodeName == 'originalAirDate':
strAirdate = shownode.toxml()
strAirdate = strAirdate.replace('<originalAirDate>','')
strAirdate = strAirdate.replace('</originalAirDate>','')
strAirdate = strAirdate[:10]
#print 'Airdate: '+strAirdate
# now that we have the title, episode, genre and description, create a showname string depending on which ones you have
# if there is no episode name use the description in the title
if len(strEpisode) == 0:
strShowname = strTitle+' - '+strDescription
strPlot = strDescription
# else if there is an episode use that
elif len(strEpisode) > 0:
if name == 'All Shows' or name == 'Sports':
strShowname = strTitle+' - '+strEpisode
elif name != 'All Shows' and name != 'Sports':
strShowname = strEpisode
strPlot = strDescription
#print strShowname
if shownode.nodeName == 'airing':
for shownode1 in shownode.childNodes:
if shownode1.nodeName == 'mediafile':
for shownode2 in shownode1.childNodes:
if shownode2.nodeName == 'segmentList':
shownode3 = shownode2.childNodes[1]
print shownode3.getAttribute('filePath')
strFilepath = shownode3.getAttribute('filePath')
addLink(strShowname,strFilepath.replace('D:\\SageTV\\','smb://sagetv/SageRecordings/'),strPlot,'',strGenre,strAirdate,strTitle)
def get_params():
param=[]
paramstring=sys.argv[2]
if len(paramstring)>=2:
params=sys.argv[2]
cleanedparams=params.replace('?','')
if (params[len(params)-1]=='/'):
params=params[0:len(params)-2]
pairsofparams=cleanedparams.split('&')
param={}
for i in range(len(pairsofparams)):
splitparams={}
splitparams=pairsofparams[i].split('=')
if (len(splitparams))==2:
param[splitparams[0]]=splitparams[1]
return param
def addLink(name,url,plot,iconimage,genre,airdate,showtitle):
#print 'This is when addlink gets called'
#xbmcplugin.setContent(int(sys.argv[1]),'Episodes')
#xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
# print name
# print url
# print plot
# print iconimage
ok=True
liz=xbmcgui.ListItem(name)
liz.setInfo( type="Video", infoLabels={ "Title": name, "Plot": plot, "Genre": genre, "aired": airdate, "TVShowTitle": showtitle } )
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=False)
return ok
def addDir(name,url,mode,iconimage):
#print sys.argv[0]
# This line makes the plugin call the main default.py with three arguments, the url, the mode, and the name
# Since it calls it with arguments, when the get_params function runs first it creates an array with the arguments in it, so that url, name, and mode get assigned in the try section
# Then since those are assigned and now mode = 2 it calls videolinks with url and name as arguments, which then lists all the files
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name)
#print "u: "+u
ok=True
liz=xbmcgui.ListItem(name)
#liz.setInfo( type="Episodes", infoLabels={ "Title": name } )
#liz.setIconImage('laworder.png')
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
return ok
params=get_params()
url=None
name=None
mode=None
try:
url=urllib.unquote_plus(params["url"])
except:
pass
try:
name=urllib.unquote_plus(params["name"])
except:
pass
try:
mode=int(params["mode"])
except:
pass
print "Mode: "+str(mode)
print "URL: "+str(url)
print "Name: "+str(name)
if mode==None or url==None or len(url)<1:
print ""
CATEGORIES()
elif mode==1:
print ""+url
INDEX(url)
elif mode==2:
print ""+url
VIDEOLINKS(url,name)
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_TITLE)
xbmcplugin.setContent(int(sys.argv[1]),'episodes')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
You'll also need the addon.xml to make XBMC see the addon. Code below:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.program.SageTV"
name="SageTV"
version="1.0.0"
provider-name="">
<requires>
<import addon="xbmc.python" version="1.0"/>
</requires>
<extension point="xbmc.python.pluginsource"
library="default.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<summary>SageTV plugin</summary>
<description>connect to SageTV library</description>
</extension>
</addon>
Hope this works for you, let me know if you have questions.