Here's the addon I wrote. I didn't know how to package it so it requires manual installation, if anyone wants to make it an installable zip that would be awesome. This requires having the SageTV webserver installed. I've tested it with SageTV 6 and 7.
Create a new folder in your addons directory named SageTV, and save this code into Default.py. Lines 7 and 99 need to be edited for your setup with username, password, IP address and port. Line 76 needs your paths for where SageTV files are stored locally and the network address. For the IP stuff in lines 7 and 99 I made it generic, line 76 I left with my info so you can see how to set it up
Lines 9 and 10 that are commented out have some examples of how you can create subdirectories for your shows based on show title, genre, or anything else you want, just perform the search in the Sage web interface and copy/paste the URL into the addon code.
Code:
import urllib,urllib2,re
import xbmc,xbmcplugin,xbmcgui
import os
from xml.dom.minidom import parse
def CATEGORIES():
strUrl = 'http://user:password@sage IP:port'
addDir('All Shows', strUrl + '/sage/Recordings?xml=yes',2,'icon.png')
#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,'dailyshow.jpg')
#addDir('Sports',strUrl + '/sage/Search?searchType=TVFiles&Categories=Sports+event&SearchString=&xml=yes',2,'sports.jpg')
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
#This code parses the xml link
req = urllib.urlopen(url)
content = parse(req)
for showlist in content.getElementsByTagName('show'):
strTitle = ''
strEpisode = ''
strDescription = ''
strGenre = ''
strAirdate = ''
strMediaFileID = ''
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('&','&')
# Get the episode name
if shownode.nodeName == 'episode':
strEpisode = shownode.toxml()
strEpisode = strEpisode.replace('<episode>','')
strEpisode = strEpisode.replace('</episode>','')
strEpisode = strEpisode.replace('&','&')
# Get the show description
if shownode.nodeName == 'description':
strDescription = shownode.toxml()
strDescription = strDescription.replace('<description>','')
strDescription = strDescription.replace('</description>','')
strDescription = strDescription.replace('&','&')
# 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('&','&')
# 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]
# 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
if shownode.nodeName == 'airing':
for shownode1 in shownode.childNodes:
if shownode1.nodeName == 'mediafile':
strMediaFileID = shownode1.getAttribute('sageDbId')
for shownode2 in shownode1.childNodes:
if shownode2.nodeName == 'segmentList':
shownode3 = shownode2.childNodes[1]
strFilepath = shownode3.getAttribute('filePath')
addLink(strShowname,strFilepath.replace('D:\\SageTV\\','smb://sagetv/SageRecordings/'),strPlot,'',strGenre,strAirdate,strTitle,strMediaFileID)
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,fileid):
ok=True
liz=xbmcgui.ListItem(name)
strDelete = 'http://user:password@sage IP:port/sagex/api?command=DeleteFile&1=mediafile:' + fileid
liz.addContextMenuItems([('Delete', 'PlayMedia(' + strDelete + ')',)])
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):
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name)
ok=True
liz=xbmcgui.ListItem(name)
liz.setInfo(type="TV Show", infoLabels={ "Title": name } )
liz.setIconImage(xbmc.translatePath(os.path.join(os.getcwd().replace(';', ''),'resources','media',iconimage)))
liz.setThumbnailImage(xbmc.translatePath(os.path.join(os.getcwd().replace(';', ''),'resources','media',iconimage)))
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
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]))
Then you need to create a file called addon.xml in that same directory, with the following content:
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>
you can also create a subdirectory in the addon folder called /resources/media and save images there so that folders have artwork. You'll see in the addon code I have icon.png, dailyshow.png, etc. Put images there and put the appropriate name in the code and your categories you add will get images. I just had the SageTV icon saved as icon.png for the main listing,
I hope this is clear enough, it was my first addon and I was able to write the code but never learned how to properly package it. It should be very straightforward to setup, but let me know if you have questions.
If anyone that wants to package this into an easily installed addon and/or improve the code that would be great