Help with SageTV plugin

I’m trying to build a plugin for SageTV. For now I just want very basic functionality, list and play recordings. The SageTV webserver can return XML files with info on all of the recordings by passing a URL, and if I enter that URL in Safari I get the XML. The plugin is throwing this error when trying to connect:



22:48:38.260771: com.plexapp.plugins.sagetv              :   (Framework) Bundle verification complete<br />
22:48:38.261389: com.plexapp.plugins.sagetv              :   (Framework) Debugging is enabled<br />
22:48:38.261451: com.plexapp.plugins.sagetv              :   (Framework) Default encoding is utf-8<br />
22:48:38.263697: com.plexapp.plugins.sagetv              :   (Framework) Loaded en strings<br />
22:48:38.263824: com.plexapp.plugins.sagetv              :   (Framework) Couldn't find en-us strings<br />
22:48:38.263862: com.plexapp.plugins.sagetv              :   (Framework) Configured framework modules<br />
22:48:38.268418: com.plexapp.plugins.sagetv              :   (Framework) Imported plug-in module<br />
22:48:38.268627: com.plexapp.plugins.sagetv              :   (Framework) Loaded shared resource named 'trust.json'<br />
22:48:38.273011: com.plexapp.plugins.sagetv              :   (Framework) Checked module imports<br />
22:48:38.273517: com.plexapp.plugins.sagetv              :   (Framework) Loaded the dictionary file<br />
22:48:38.273718: com.plexapp.plugins.sagetv              :   (Framework) No cookie jar found<br />
22:48:38.319371: com.plexapp.plugins.sagetv              :   (Framework) Initialized framework modules<br />
22:48:38.319440: com.plexapp.plugins.sagetv              :   (Framework) Attempting to start the plug-in...<br />
22:48:38.319501: com.plexapp.plugins.sagetv              :   (Framework) Added a handler for prefix '/video/sagetv'<br />
22:48:38.402201: com.plexapp.plugins.sagetv              :   (Framework) Added a view group named 'InfoList'<br />
22:48:38.403524: com.plexapp.plugins.sagetv              :   (Framework) Plug-in started<br />
22:48:38.403923: com.plexapp.plugins.sagetv              :   (Framework) Entering run loop<br />
22:48:38.404165: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /:/prefixes<br />
22:48:38.404456: com.plexapp.plugins.sagetv              :   (Framework) Resource named 'icon-default.png' of type 'image/png' was made public.<br />
22:48:38.404655: com.plexapp.plugins.sagetv              :   (Framework) Resource named 'art-default.png' of type 'image/png' was made public.<br />
22:48:38.404958: com.plexapp.plugins.sagetv              :   (Framework) Response OK<br />
22:48:38.406530: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /:/prefixes<br />
22:48:38.407137: com.plexapp.plugins.sagetv              :   (Framework) Response OK<br />
<br />
22:50:31.752547: com.plexapp.plugins.sagetv              :   (Framework) Loaded en strings<br />
22:50:31.752934: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /video/sagetv<br />
22:50:31.753224: com.plexapp.plugins.sagetv              :   (Framework) Response OK<br />
22:50:34.265619: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /video/sagetv/:/function/all_shows/KGRwMApTJ3BhZ2VVcmwnCnAxClMnaHR0cDovL2Fhcm9uOmVybmllMTk4MUAxOTIuMTY4LjAuMTA6ODA4My9zYWdlL1JlY29yZGluZ3M@eG1sPXllcycKcDIKc1Mnc2VuZGVyJwpwMwpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDQKKGNQTVMuT2JqZWN0cwpJdGVtSW5mb1JlY29yZApwNQpjX19idWlsdGluX18Kb2JqZWN0CnA2Ck50cDcKUnA4CihkcDkKUydpdGVtVGl0bGUnCnAxMApTJ1Nob3dzJwpwMTEKc1MndGl0bGUxJwpwMTIKTnNTJ3RpdGxlMicKcDEzCk5zUydhcnQnCnAxNApOc2JzLg__<br />
22:50:34.266820: com.plexapp.plugins.sagetv              :   (Framework) Calling named function 'all_shows'<br />
22:50:34.268432: com.plexapp.plugins.sagetv              :   (Framework) URLError when requesting 'http://username:password@192.168.0.10:8083/sage/Recordings?xml=yes'<br />
22:50:34.268649: com.plexapp.plugins.sagetv              :   (Framework) An exception happened:<br />
Traceback (most recent call last):<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Plugin.py", line 640, in __call<br />
    return function(*args, **kwargs)<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/SageTV.bundle/Contents/Code/__init__.py", line 42, in all_shows<br />
    for item in content.xpath('/showlist//segment'):<br />
AttributeError: 'NoneType' object has no attribute 'xpath'<br />
<br />
22:50:34.268735: com.plexapp.plugins.sagetv              :   (Framework) Request not handled by plug-in<br />
<br />
22:50:36.973326: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /video/sagetv/:/function/all_shows/KGRwMApTJ3BhZ2VVcmwnCnAxClMnaHR0cDovL2Fhcm9uOmVybmllMTk4MUAxOTIuMTY4LjAuMTA6ODA4My9zYWdlL1JlY29yZGluZ3M@eG1sPXllcycKcDIKc1Mnc2VuZGVyJwpwMwpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDQKKGNQTVMuT2JqZWN0cwpJdGVtSW5mb1JlY29yZApwNQpjX19idWlsdGluX18Kb2JqZWN0CnA2Ck50cDcKUnA4CihkcDkKUydpdGVtVGl0bGUnCnAxMApTJ1Nob3dzJwpwMTEKc1MndGl0bGUxJwpwMTIKTnNTJ3RpdGxlMicKcDEzCk5zUydhcnQnCnAxNApOc2JzLg__<br />
22:50:36.974433: com.plexapp.plugins.sagetv              :   (Framework) Calling named function 'all_shows'<br />
**22:50:36.975129: com.plexapp.plugins.sagetv              :   (Framework) URLError when requesting 'http://username:password@192.168.0.10:8083/sage/Recordings?xml=yes'**<br />
22:50:36.975399: com.plexapp.plugins.sagetv              :   (Framework) An exception happened:<br />
Traceback (most recent call last):<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Plugin.py", line 640, in __call<br />
    return function(*args, **kwargs)<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/SageTV.bundle/Contents/Code/__init__.py", line 42, in all_shows<br />
    for item in content.xpath('/showlist//segment'):<br />
AttributeError: 'NoneType' object has no attribute 'xpath'<br />
<br />
22:50:36.975490: com.plexapp.plugins.sagetv              :   (Framework) Request not handled by plug-in



I am complete novice with Python, I copied some code form a different plugin to get started. The very limited pieces I've built so far seem to be working OK. Code is below. My username and password are hardcoded for now, changed in the pasted code for obvious reasons. Thanks in advance for any help


####################################################################################################<br />
from PMS import *<br />
from PMS.Objects import *<br />
from PMS.Shortcuts import *<br />
<br />
####################################################################################################<br />
<br />
VIDEO_PREFIX = "/video/sagetv"<br />
<br />
SAGE_URL                     = "http://username:password@192.168.0.10:8083/sage/Recordings?xml=yes"<br />
SAGE_WATCH_URL               = "http://username:password@192.168.0.10:8083/sage/Recordings?xml=yes"<br />
CACHE_INTERVAL              = 3600<br />
DEBUG                       = True<br />
ART           = 'art-default.png'<br />
ICON          = 'icon-default.png'<br />
<br />
####################################################################################################<br />
<br />
def Start():<br />
  Plugin.AddPrefixHandler(VIDEO_PREFIX, MainMenu, L("SageTV Plugin"))<br />
  Plugin.AddViewGroup("InfoList", viewMode="InfoList", mediaType="items")<br />
<br />
####################################################################################################<br />
def MainMenu():<br />
    dir = MediaContainer(mediaType='video')<br />
    dir.Append(<br />
              Function(<br />
                       DirectoryItem(<br />
                         all_shows,<br />
                         title="Shows",<br />
                         summary="Browse Show List"<br />
                       ),<br />
                   pageUrl = SAGE_WATCH_URL<br />
                   )<br />
              )<br />
    return dir<br />
    <br />
####################################################################################################<br />
def all_shows(sender, pageUrl):<br />
    dir = MediaContainer(title2=sender.itemTitle)<br />
    content = XML.ElementFromURL(pageUrl, True)<br />
    for item in content.xpath('/showlist//segment'):<br />
      titleUrl = item.get('filePath')<br />
      Log(titleUrl)<br />
      dir.Append(Function(DirectoryItem(VideoPage, titleUrl), titleUrl = SAGE_URL+titleUrl))<br />
    return dir <br />
<br />
####################################################################################################<br />
def VideoPage(sender, titleUrl):<br />
    dir = MediaContainer(title2=sender.itemTitle)<br />
    content = XML.ElementFromURL(titleUrl, True)<br />
    for item2 in content.xpath('//div[@class="img"]//a'):<br />
        vidUrl = item2.xpath(".")[0].get('href')<br />
        vidUrl = SAGE_URL+vidUrl       <br />
        Log(vidUrl)<br />
        image2 = item2.xpath(".//img")[0].get('src')<br />
        Log(image2)<br />
        title2 = item2.xpath(".")[0].get('href')<br />
        Log(title2)<br />
        dir.Append(WebVideoItem(vidUrl, thumb=image2, title=title2))<br />
    return dir

When I get none type errors it’s because I’m anot actually pulling in an XML element. WhY are there 2 forward slashes after show list?

This stands out.






If it's pure XML, you don't need "True" in your ElementFromURL calls... That's making it look for HTML

Try:

<br />
    content = XML.ElementFromURL(pageUrl)<br />
    

Thanks guys, will try those things when I get home.



I was using the example syntax here: http://www.w3schools.com/xpath/xpath_syntax.asp, specifically this one

bookstore//book Selects all book elements that are descendant of the bookstore element, no matter where they are under the bookstore element

The segment element is not immediately after the showlist element so I thought I needed both slashes. I'm a novice at parsing XML too. I will try it without the double slash.

If you want all the segment elements that are inside the showlist element, but not direct children, you are doing it right :slight_smile: (and even if they we’re direct children you would have found them with the double slash too).

I was still getting the same error, and the first error (URLError when requesting 'http: etc) made it look like it wasn’t able to get the xml file from my server. I tried saving it to a shared directory and using a smb:// url but it didn’t like that either. I moved a copy of the xml file to my webserver and that worked, so apparently it doesn’t like having the username/password in the url?



I can setup SageTV to automatically generate the xml file and save it in an accessible location, but how would I point the plugin to the file? Using XML.ElementFromURL(smb://server/sharedfolder/file.xml) fails.



For the meantime I’ll keep working with the remote copy, and try to get it to parse the xml properly to list all the shows.

It seems to load the xml OK, but it’s not returning anything from it, including when I tell it to log something for debugging purposes. Code is below, anything obvious I’m doing wrong? The xml file I’m using is at www.aaronblackshear.com/recordings.xml



####################################################################################################<br />
# PMS plugin framework<br />
from PMS import *<br />
from PMS.Objects import *<br />
from PMS.Shortcuts import *<br />
<br />
VIDEO_PREFIX = "/video/sagetv"<br />
<br />
SAGE_WATCH_URL               = "http://www.aaronblackshear.com/recordings.xml"<br />
CACHE_INTERVAL              = 3600<br />
DEBUG                       = True<br />
ART           = 'art-default.png'<br />
ICON          = 'icon-default.png'<br />
<br />
####################################################################################################<br />
<br />
def Start():<br />
  Plugin.AddPrefixHandler(VIDEO_PREFIX, MainMenu, L("VBS"))<br />
  Plugin.AddViewGroup("InfoList", viewMode="InfoList", mediaType="items")<br />
<br />
####################################################################################################<br />
def MainMenu():<br />
    dir = MediaContainer(mediaType='video')<br />
    dir.Append(<br />
              Function(<br />
                       DirectoryItem(<br />
                         all_shows,<br />
                         title="Shows",<br />
                         summary="Browse Show List"<br />
                       ),<br />
                   pageUrl = SAGE_WATCH_URL<br />
                   )<br />
              )<br />
    return dir<br />
    <br />
####################################################################################################<br />
def all_shows(sender, pageUrl):<br />
    dir = MediaContainer(title2=sender.itemTitle)<br />
    content = XML.ElementFromURL(pageUrl)<br />
    for item in content.xpath('/showlist//segment'):<br />
      titleUrl = item.get('filePath')<br />
      Log(titleUrl)<br />
      dir.Append(Function(VideoItem(key=titleUrl)))<br />
    return dir



And the log:


19:06:28.981875: com.plexapp.plugins.sagetv              :   (Framework) Loaded en strings<br />
19:06:28.982323: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /video/sagetv<br />
19:06:28.982588: com.plexapp.plugins.sagetv              :   (Framework) Response OK<br />
19:06:32.021434: com.plexapp.plugins.sagetv              :   (Framework) Handling request :  /video/sagetv/:/function/all_shows/KGRwMApTJ3BhZ2VVcmwnCnAxClMnaHR0cDovL3d3dy5hYXJvbmJsYWNrc2hlYXIuY29tL3JlY29yZGluZ3MueG1sJwpwMgpzUydzZW5kZXInCnAzCmNjb3B5X3JlZwpfcmVjb25zdHJ1Y3RvcgpwNAooY1BNUy5PYmplY3RzCkl0ZW1JbmZvUmVjb3JkCnA1CmNfX2J1aWx0aW5fXwpvYmplY3QKcDYKTnRwNwpScDgKKGRwOQpTJ2l0ZW1UaXRsZScKcDEwClMnU2hvd3MnCnAxMQpzUyd0aXRsZTEnCnAxMgpOc1MndGl0bGUyJwpwMTMKTnNTJ2FydCcKcDE0Ck5zYnMu<br />
19:06:32.022656: com.plexapp.plugins.sagetv              :   (Framework) Calling named function 'all_shows'<br />
19:06:32.660931: com.plexapp.plugins.sagetv              :   (Framework) Received response from http://www.aaronblackshear.com/recordings.xml<br />
19:06:32.679777: com.plexapp.plugins.sagetv              :   (Framework) Response OK<br />
<br />
19:06:37.662486: com.plexapp.plugins.sagetv              :   (Framework) Saved shared HTTP data

your xpath is the culprit.

your tag is actually “showList” not “showlist”



you can use:


<br />
for item in content.xpath('//showList//segment'):


NOTE: you need to begin with "//" because "showList" is not a root node.

or maybe even just use "segment" unless you plan on using the "segment" tag further in your XML page:

for item in content.xpath('//segment'):

Ahh, what a silly mistake, I hoped it wasn’t something so obvious. I thought I’d checked all the case/spelling. I was worried there was something about the xml file SageTV was producing that Plex didn’t like. Thanks again for your help, I really appreciate it.



I tried it with //segment and it’s logging the list now, so I know it’s reading the file properly. Progress! (I also just spent some time pulling hair out when the plugin stopped showing up in Plex, I learned through excruciating trial and error that if your init.pyc does not get updated when you change your code something is wrong with your code, and that python does not like backslashes in a string!)



It’s listing my filenames translated with the network address in the log file, and then throwing this error about an object not having an attribute name:



File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Plugin.py", line 497, in __run<br />
    resultStr = result.Content()<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Objects.py", line 132, in Content<br />
    return XML.StringFromElement(self.ToElement())<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Objects.py", line 255, in ToElement<br />
    __itemElement = item.ToElement()<br />
  File "/Users/aaron/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Python/PMS/Objects.py", line 414, in ToElement<br />
    self.key = "%s/:/function/%s/%s" % (Plugin.CurrentPrefix(), self.__obj.key.__name__, "%s%s" % (encodedArgs, self.__ext))<br />
AttributeError: 'str' object has no attribute '__name__'<br />
<br />
<br />
20:50:01.592024: com.plexapp.plugins.sagetv              :   (Framework) Saved shared HTTP data



Current code:


####################################################################################################<br />
# PMS plugin framework<br />
from PMS import *<br />
from PMS.Objects import *<br />
from PMS.Shortcuts import *<br />
<br />
VIDEO_PREFIX = "/video/sagetv"<br />
<br />
SAGE_WATCH_URL               = "http://www.aaronblackshear.com/recordings.xml"<br />
CACHE_INTERVAL              = 3600<br />
DEBUG                       = True<br />
ART           = 'art-default.png'<br />
ICON          = 'icon-default.png'<br />
<br />
####################################################################################################<br />
<br />
def Start():<br />
  Plugin.AddPrefixHandler(VIDEO_PREFIX, MainMenu, L("SageTVPlugin"))<br />
  Plugin.AddViewGroup("InfoList", viewMode="InfoList", mediaType="items")<br />
<br />
####################################################################################################<br />
def MainMenu():<br />
    dir = MediaContainer(mediaType='video')<br />
    dir.Append(<br />
              Function(<br />
                       DirectoryItem(<br />
                         all_shows,<br />
                         title="Shows",<br />
                         summary="Browse Show List"<br />
                       ),<br />
                   pageUrl = SAGE_WATCH_URL<br />
                   )<br />
              )<br />
    return dir<br />
    <br />
####################################################################################################<br />
def all_shows(sender, pageUrl):<br />
    dir = MediaContainer(title2=sender.itemTitle)<br />
    content = XML.ElementFromURL(pageUrl)<br />
    for item in content.xpath('//segment'):<br />
      titleUrl = item.get('filePath')<br />
      titleUrl = titleUrl.replace('D:\SageTV\','smb://sagetv/SageRecordings/')<br />
      Log(titleUrl)<br />
      dir.Append(Function(VideoItem(key=titleUrl, title=titleUrl)))<br />
    return dir

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.