示例#1
0
def AddViewGroup(name, viewMode="list", contentType="items"):
  global ViewGroups
  if viewMode in MediaXML.ViewModes.keys():
    Log.Add("(Framework) Adding view group '%s'" % name)
    ViewGroups[name] = {"ViewMode": str(MediaXML.ViewModes[viewMode]), "ContentType": contentType}
  else:
    Log.Add("(Framework) Couldn't create view group '%s' - invalid view mode." % name)
示例#2
0
def Open(url, values=None, headers={}):
    """
    Returns the response of a HTTP request using the given URL and values
    
    @param url: The URL to request data from
    @type url: string
    @param values: The values to post as part of the request
    @type values: dictionary
    @param headers: Extra headers to send when making the request (optional)
    @type headers: dictionary
    @return: string
  """
    # TODO: Add gzip support
    try:
        data = None
        if values is not None: data = urllib.urlencode(values)
        h = __headers.copy()
        for header in headers:
            h[header] = headers[header]
        request = urllib2.Request(url, data, __headers)
        response = urllib2.urlopen(request)
        if __cookieJar is not None:
            __cookieJar.save(Plugin.DataPath + __cookieFile)
        return response
    except urllib2.HTTPError:
        Log.Add("(Framework) HTTPError when requesting '%s'" % url)
        return None
    except urllib2.URLError:
        Log.Add("(Framework) URLError when requesting '%s'" % url)
        return None
示例#3
0
def GetCached(url, cacheTime=1800, headers={}, forceUpdate=False):
    if not Plugin.Dict.has_key("_PMS.HTTP.Cache"):
        Plugin.Dict["_PMS.HTTP.Cache"] = {}

    cache = Plugin.Dict["_PMS.HTTP.Cache"]
    now = datetime.datetime.now()
    if cache.has_key(url):
        cachedAt = cache[url]["CheckTime"]
        expiresAt = cachedAt + datetime.timedelta(seconds=cacheTime)
        if datetime.datetime.now() < expiresAt and forceUpdate == False:
            Log.Add("(Framework) Loaded '%s' from the cache" % url)
            return cache[url]["Content"]

    content = Get(url, headers)
    if content is not None:
        cache[url] = {}
        cache[url]["Content"] = content
        cache[url]["CheckTime"] = now
        Plugin.Dict["_PMS.HTTP.Cache"] = cache
        Plugin.Dict["_PMS.HTTP.CacheTime"] = datetime.datetime.now()
        Log.Add("(Framework) Saved '%s' to the cache" % url)
        return content
    else:
        Log.Add("(Framework) Couldn't cache '%s'" % url)
        return None
示例#4
0
文件: DB.py 项目: toxictigga/wdmcgen2
def __loadDB():
    import sqlite3
    global __db
    shouldCreateTables = not os.path.exists(Plugin.__databasePath)
    __db = sqlite3.connect(Plugin.__databasePath)
    if shouldCreateTables:
        try:
            Plugin.__call(Plugin.__pluginModule.CreateTables)
            Commit()
            Log.Add("(Framework) Created database tables")
        except AttributeError:
            Log.Add("(Framework) Error creating database tables", False)
            pass
示例#5
0
def SaveDict():
  """
    Save the plugin's dictionary to the data directory if it has been modified.
  """
  global Dict
  global __savedDict
  if Dict != __savedDict:
    Log.Add("(Framework) Dictionary has been changed")
    __savedDict = Dict.copy()
    pickle_file = DataFilePath("DictPickle")
    f = open(pickle_file, "w")
    pickle.dump(__savedDict, f, 2)
    Log.Add("(Framework) Plugin dictionary pickled")
示例#6
0
def AddRequestHandler(prefix, handler, name="", thumb="", art=""):
    """
    Add a request handler for a given path prefix. This must be a function within the plugin which
    accepts pathNouns and count as arguments. The prefix should not include a trailing '/'
    character.
    
    @param prefix: The path prefix to be handled
    @type prefix: string
    @param handler: The function that will handle the request
    @type handler: function
    @param name: The display name for the prefix
    @type prefix: string
    @param thumb: The name of an exposed resource file to use as a thumbnail
    @type thumb: string
    @param art: The name of an exposed resource file to use as artwork
    @type art: string
  """
    global __requestHandlers
    if not __requestHandlers.has_key(prefix):
        handler_info = {
            "handler": handler,
            "name": name,
            "thumb": thumb,
            "art": art
        }
        __requestHandlers[prefix] = handler_info
        Log.Add("(Framework) Adding request handler for prefix '%s'" % prefix)
示例#7
0
 def SetViewGroup(self, viewGroup):
     if Plugin.ViewGroups.has_key(viewGroup):
         self.SetAttr("viewmode", Plugin.ViewGroups[viewGroup]["ViewMode"])
         self.SetAttr("content",
                      Plugin.ViewGroups[viewGroup]["ContentType"])
     else:
         Log.Add("(Framework) Error: Invalid view group.")
示例#8
0
def LoadDict():
  """
    Load the plugin's dictionary from the data directory.
  """
  global Dict
  global __savedDict
  pickle_file = DataFilePath("DictPickle")
  try:
    if os.path.exists(pickle_file):
      f = open(pickle_file, "r")   
      __savedDict = pickle.load(f)
      f.close()
      Log.Add("(Framework) Plugin dictionary unpickled")
    else:
      __savedDict = {}
  except:
    Log.Add("(Framework) Unable to load plugin dictionary - file appears to be corrupt")
    __savedDict = {}
  Dict = __savedDict.copy()
示例#9
0
def ResetDict():
  """
    Reset the plugin's dictionary. This will erase all previously stored values.
  """
  pickle_file = DataFilePath("DictPickle")
  if os.path.exists(pickle_file):
    os.remove(pickle_file)
  Dict = {}
  __savedDict = {}
  Log.Add("(Framework) Plugin dictionary reset")
示例#10
0
def __loadDefaults():
  global __defaultLangDict
  global __defaultCountryDict
  global __defaultLocale
  pos = __defaultLocale.find("-")
  
  if pos > -1:
    lang = __defaultLocale[:pos]
    langPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % lang)
    if os.path.exists(langPath):
      __defaultLangDict = JSON.DictFromFile(langPath)
      Log.Add("(Framework) Loaded %s strings" % lang)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % lang)
    
    locPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % __defaultLocale)
    if os.path.exists(locPath):
      __defaultCountryDict = JSON.DictFromFile(locPath)
      Log.Add("(Framework) Loaded %s strings" % __defaultLocale)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % __defaultLocale)
      
  else:
    langPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % __defaultLocale)
    if os.path.exists(langPath):
      __defaultLangDict = JSON.DictFromFile(langPath)
      Log.Add("(Framework) Loaded %s strings" % __defaultLocale)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % __defaultLocale)
示例#11
0
def __loadLocale(loc):
  global CurrentLocale
  global __langDict
  global __countryDict
  pos = loc.find("-")
  
  if pos > -1:
    
    lang = loc[:pos]
    langPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % lang)
    if os.path.exists(langPath):
      __langDict = JSON.DictFromFile(langPath)
      Log.Add("(Framework) Loaded %s strings" % lang)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % lang)
    
    locPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % loc)
    if os.path.exists(locPath):
      __countryDict = JSON.DictFromFile(locPath)
      Log.Add("(Framework) Loaded %s strings" % loc)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % loc)
      
  else:
    langPath = os.path.join(Plugin.BundlePath, "Contents/Strings/%s.json" % loc)
    if os.path.exists(langPath):
      __langDict = JSON.DictFromFile(langPath)
      Log.Add("(Framework) Loaded %s strings" % loc)
    else:
      Log.Add("(Framework) Couldn't find %s strings" % loc)
      
  CurrentLocale = loc
示例#12
0
def Expose(key, description):
    """
    Make the given preference available for modification via the plugin's request handlers.
    
    @param key: The key of the preference
    @type key: string
    @param description: The description of the preference
    @type description: string
  """
    global __publicPrefs
    if not __publicPrefs.has_key(key):
        __publicPrefs[key] = description
        Log.Add("(Framework) Exposed preference '%s'" % key)
示例#13
0
def SaveDataToFile(file_name, data):
  """
    Saves data to a given file in the plugin's data directory.

    @param file_name: The name of the file
    @type file_name: string
    @param data: The data to save
    @type data: any
  """
  f = open(DataFilePath(file_name), 'w')
  f.write(data)
  f.close()
  Log.Add("(Framework) Saved data file named '%s'" % file_name)
示例#14
0
def DataFile(file_name):
  """
    Returns the data contained in the given file in the plugin's data directory.

    @param file_name: The name of the file
    @type file_name: string
    @return: data
  """
  f = open(DataFilePath(file_name), 'r')
  d = f.read()
  f.close()
  Log.Add("(Framework) Loaded data file named '%s'" % file_name)
  return d
示例#15
0
def Resource(file_name):
  """
    Returns the data contained in the given file in the Resources directory inside the plugin
    bundle.

    @param file_name: The name of the file
    @type file_name: string
    @return: data
  """
  f = open(ResourceFilePath(file_name), 'r')
  d = f.read()
  f.close()
  Log.Add("(Framework) Loaded resource named '%s'" % file_name)
  return d
示例#16
0
def __handleInternalRequest(pathNouns, count):
  #
  # Handle a request internally
  #
  global __publicResources
  if count > 1:
    if pathNouns[1] == "resources":
      if count == 3:
        if __publicResources.has_key(pathNouns[2]):
          Log.Add("Getting resource")
          resource = Resource(pathNouns[2])
          Response["Content-Type"] = __publicResources[pathNouns[2]]
          return resource

    elif pathNouns[1] == "prefs":
      return Prefs.__handleRequest(pathNouns,count)
示例#17
0
def ExposeResource(resource_name, content_type):
  """
    Make the given file in the Resources directory inside the plugin bundle available
    publically via request handlers. Returns True if the file was found or False otherwise.

    @param resource_name: The name of the file
    @type resource_name: string
    @param content_type: The content type to use when returning the data in the file
    @type content_type: string
    @return: boolean
  """
  global __publicResources
  if os.path.exists(ResourceFilePath(resource_name)) and not __publicResources.has_key(resource_name):
    __publicResources[resource_name] = content_type
    Log.Add("(Framework) Exposed resource named '%s' as '%s'" % (resource_name, content_type))
    return True
  else: return False
示例#18
0
def Set(key, value):
    """
    Sets the preference for the given key to the given value.
    
    @param key: The key of the preference
    @type key: string
    @param value: The value to set
    @type value: string
  """
    el = Plugin.__prefs.find(key)
    if el is None:
        el = XML.Element(key)
        el.text = value
        Plugin.__prefs.append(el)
    else:
        el.text = value
    XML.ElementToFile(Plugin.__prefs, Plugin.__prefsPath)
    Log.Add("(Framework) Set preference '%s' to '%s'", (key, value))
示例#19
0
def ElementFromURL(url, use_html_parser=False):
    """
    Creates a new Element object with the response from the given URL.
    
    @param url: The URL used in the request
    @type url: string
    @param use_html_parser: Specifies whether to parse the response as HTML instead of XML
    @type use_html_parser: boolean
    @return: Element
  """
    text = HTTP.Get(url)
    if text is not None:
        Log.Add('(Framework) Request to %s return %d bytes' % (url, len(text)))
        if use_html_parser:
            try:
                root = html.fromstring(text)
                test = html.tostring(root, encoding=unicode)
                return root
            except:
                return fromstring(text)
        else:
            return etree.fromstring(text)
    else:
        return None
示例#20
0
def SetTimeout(timeout):
    socket.setdefaulttimeout(timeout)
    Log.Add("(Framework) Set the default socket timeout to %.1f seconds" %
            timeout)
示例#21
0
def __run(_bundlePath):
  #
  # Initializes the plugin framework, verifies the plugin & extracts information, then enters a
  # run loop for handling requests. 
  #
  global BundlePath
  global ResourcesPath
  global Identifier
  global DataPath
  global Debug
  
  global __pluginModule
  global __prefs
  global __prefsPath
  global __databasePath
  global __logFilePath
  global __requestHandlers
  
  if sys.platform == "win32":
    if 'PLEXLOCALAPPDATA' in os.environ:
      key = 'PLEXLOCALAPPDATA'
    else:
      key = 'LOCALAPPDATA'
    supportFilesPath = os.path.join(os.environ[key], "Plex Media Server", "Plug-in Support")
    logFilesPath = os.path.join(os.environ[key], "Plex Media Server", "Logs", "PMS Plugin Logs")
  else:
    supportFilesPath = os.environ["HOME"] + "/Library/Application Support/Plex Media Server/Plug-in Support"
    logFilesPath = os.environ["HOME"] + "/Library/Logs/Plex Media Server/PMS Plugin Logs/"
  
  def checkpath(path):
    try:
      if not os.path.exists(path): os.makedirs(path)
    except:
      pass
  
  checkpath(supportFilesPath + "/Preferences")
  checkpath(supportFilesPath + "/Databases")
  checkpath(logFilesPath)
  
  # Set the bundle path variable
  BundlePath = _bundlePath.rstrip('/')
  ResourcesPath = BundlePath + "/Contents/Resources"
  
  # Add the bundle path to the system path
  if os.path.isdir(BundlePath + "/Contents"):
    sys.path.append(BundlePath + "/Contents")
    if os.path.isdir(BundlePath + "/Contents/Libraries"):
      sys.path.append(BundlePath + "/Contents/Libraries")
  else:
    print "Couldn't find bundle directory"
    return None
  
  # Open the Info.plist file
  infoplist = XML.ElementFromFile((BundlePath + "/Contents/Info.plist"))
  if infoplist is None:
    print "Couldn't load Info.plist file from plugin"
    return

  # Get the plugin identifier
  Identifier = infoplist.xpath('//key[text()="CFBundleIdentifier"]//following-sibling::string/text()')[0]
  if Identifier is None:
    print "Invalid Info.plist file in plugin"
    return None
    
  # Set up the log file
  __logFilePath = logFilesPath + Identifier + ".log"
  if os.path.exists(__logFilePath):
    if os.path.exists(__logFilePath + ".old"):
      os.remove(__logFilePath + ".old")
    os.rename(__logFilePath, __logFilePath + ".old")
  
  # Show a big warning message - Framework v0 is deprecated!!
  Log.Add("(Framework) Deprecated version\n\nNOTICE: This version of the Plex Media Framework is deprecated and is no longer supported.\nPlease migrate your code to a newer version. More information can be found at http://dev.plexapp.com/\n")
  
  Log.Add("(Framework) Plugin initialized", False)
  
  # Check whether debugging is enabled
  try:
    _debug = infoplist.xpath('//key[text()="PlexPluginDebug"]//following-sibling::string/text()')[0]
    if _debug == "1":
      Debug = True
      Log.Add("(Framework) Debugging is enabled")
  except: pass
  
  # Create the data path if it doesn't already exist
  DataPath = supportFilesPath + "/Data/" + Identifier
  if not os.path.isdir(DataPath):
    os.makedirs(DataPath)
  
  # Change directory to the data path
  os.chdir(DataPath)
  
  # If a preference file exists, load it
  __prefsPath = supportFilesPath + "/Preferences/" + Identifier + ".xml"
  defaultsPath = BundlePath + "/Contents/Defaults.xml"
  if os.path.exists(__prefsPath):
    __prefs = XML.ElementFromFile(__prefsPath)
    Log.Add("(Framework) Loaded user preferences")

  # Otherwise, try to apply the defaults file
  elif os.path.exists(defaultsPath):
    __prefs = XML.ElementFromFile(defaultsPath)
    Log.Add("(Framework) Loaded default preferences")
    
  # If no preferences were loaded, create an empty preferences element
  else:
    __prefs = XML.Element("PluginPreferences")
  
  # Load the plugin's dictionary file
  LoadDict()
  
  # Load the plugin's localization strings
  Locale.__loadDefaults()
  # TODO: Retrieve locale info from PMS for overriding default dict strings 
  # Locale.__loadLocale(loc)
  
  # Set the database file path
  __databasePath = supportFilesPath + "/Databases/" + Identifier + ".db"
  
  # Initialize the plugin's CookieJar
  HTTP.__loadCookieJar()
  Log.Add("(Framework) Loaded cookie jar")
  
  if HTTP_TIMEOUT_VAR_NAME in os.environ:
    HTTP.SetTimeout(float(os.environ[HTTP_TIMEOUT_VAR_NAME]))
  else:
    HTTP.SetTimeout(HTTP_DEFAULT_TIMEOUT)
  
  # Attempt to import the plugin module - if debugging is enabled, don't catch exceptions
  if Debug:
    import Code as _plugin
    Log.Add("(Framework) Imported plugin module")
  else:
    try:
      import Code as _plugin
      Log.Add("(Framework) Imported plugin module")
    except ImportError:
      Log.Add("(Framework) Couldn't import plugin from bundle")
      return
  
  __pluginModule = _plugin
  
  # Call the plugin's Start method
  Log.Add("(Framework) Attempting to start the plugin...")
  __call(_plugin.Start)
  Log.Add("(Framework) Plugin started", False)


  Log.Add("(Framework) Entering run loop")
  # Enter a run loop to handle requests
  while True:
    try:
      # Read the input
      path = raw_input()
      
      # Strip GET from the start of the path
      path = path.lstrip("GET ").strip()
      
      # Split the path into components and decode.
      pathNouns = path.replace('?query=', '/').split('/')
      pathNouns = [urllib.unquote(p) for p in pathNouns]
      
      # If no input was given, return an error
      if len(pathNouns) <= 1:
        sys.stdout.write("%s\r\n\r\n" % PMS.Error['BadRequest'])
        sys.stdout.flush()
        
      # Otherwise, attempt to handle the request
      else:
        Response['Content-Type'] = 'application/xml'
        Response['Status'] = '200 OK'
        Response["Headers"] = ""
        result = None
        pathNouns.pop(0)
        count = len(pathNouns)
        if pathNouns[count-1] == "":
          count = count - 1
          pathNouns.pop(len(pathNouns)-1)
          
        Log.Add("(Framework) Handling request :  " + path, False)
        
        # Check for a management request
        if pathNouns[0] == ":":
          result = __handlePMSRequest(pathNouns, count)

        else:  
          # Check each request handler to see if it handles the current prefix
          handler = None
          for key in __requestHandlers:
            if handler is None:
              if path.count(key, 0, len(key)) == 1:
                # Remove the prefix from the path
                keyNounCount = len(key.split('/')) - 1
                for i in range(keyNounCount):
                  pathNouns.pop(0)
                count = count - keyNounCount
                # Find the request handler
                handler = __requestHandlers[key]["handler"]
                
          # Check whether we should handle the request internally
          handled = False
          if count > 0:
            if pathNouns[0] == ":":
              handled = True
              result = __handleInternalRequest(pathNouns, count)
    
          # If the request hasn't been handled, and we have a valid request handler, call it
          if not handled and handler is not None:
            result = handler(pathNouns, count)
        
        # If the request wasn't handled, return an error
        if result == None:
          Log.Add("(Framework) Request not handled by plugin", False)
          response = "%s\r\n\r\n" % PMS.Error['NotFound']
          
        # If the plugin returned an error, return it to PMS
        elif result in PMS.Error.values():
          Log.Add("(Framework) Plug-in returned an error :  %s" % result, False)
          response = result + "\r\n"
          
        # Otherwise, return the result
        else:
          Log.Add("(Framework) Response OK")
          response = "%s\r\nContent-Type: %s\r\nContent-Length: %i\r\n%s\r\n%s\r\n" % \
            (Response["Status"], str(Response['Content-Type']), len(result), Response["Headers"], result)

        sys.stdout.write(response)
        sys.stdout.flush()
    
    # If a KeyboardInterrupt (SIGINT) is raised, stop the plugin
    except KeyboardInterrupt:
      # Commit any changes to the database and close it
      if DB.__db is not None:
        DB.Commit()
        DB.__db.close()
      # Save the dictionary
      SaveDict()
      # Exit
      sys.exit()
    
    except EOFError:
      # Commit any changes to the database and close it
      if DB.__db is not None:
        DB.Commit()
        DB.__db.close()
      Log.Add("(Framework) Plugin stopped")
      sys.exit()
          
    # If another exception is raised, deal with the problem
    except:
      # If in debug mode, print the traceback, otherwise report an internal error
      if Debug:
        Log.Add("(Framework) An exception happened:\n%s" % traceback.format_exc())
      else:
        Log.Add("(Framework) An internal error occurred", False)
      sys.stdout.write("%s\r\n\r\n" % PMS.Error['InternalError'])
      sys.stdout.flush()
    
    # Make sure the plugin's dictionary is saved
    finally:
      SaveDict()