Moved many functions to PlexFunctions.py

This commit is contained in:
tomkat83 2016-01-27 16:33:02 +01:00
parent e5311981b4
commit 40c8b6f683
5 changed files with 255 additions and 211 deletions

View file

@ -60,29 +60,6 @@ try:
except ImportError:
import xml.etree.ElementTree as etree
def XbmcItemtypes():
return ['photo', 'video', 'audio']
def PlexItemtypes():
return ['photo', 'video', 'audio']
def PlexLibraryItemtypes():
return ['movie', 'show']
# later add: 'artist', 'photo'
def XbmcPhoto():
return "photo"
def XbmcVideo():
return "video"
def XbmcAudio():
return "audio"
def PlexPhoto():
return "photo"
def PlexVideo():
return "video"
def PlexAudio():
return "music"
@utils.logging
class PlexAPI():
@ -1381,154 +1358,6 @@ class PlexAPI():
})
return serverlist
def GetPlexCollections(self, mediatype):
"""
Input:
mediatype String or list of strings with possible values
'movie', 'show', 'artist', 'photo'
Output:
List with an entry of the form:
{
'name': xxx Plex title for the media section
'type': xxx Plex type: 'movie', 'show', 'artist', 'photo'
'id': xxx Plex unique key for the section (1, 2, 3...)
'uuid': xxx Other unique Plex key, e.g.
74aec9f2-a312-4723-9436-de2ea43843c1
}
Returns an empty list if nothing is found.
"""
collections = []
url = "{server}/library/sections"
jsondata = self.doUtils.downloadUrl(url)
try:
result = jsondata['_children']
except KeyError:
pass
else:
for item in result:
contentType = item['type']
if contentType in mediatype:
name = item['title']
contentId = item['key']
uuid = item['uuid']
collections.append({
'name': name,
'type': contentType,
'id': str(contentId),
'uuid': uuid
})
return collections
def GetPlexSectionResults(self, viewId, headerOptions={}):
"""
Returns a list (raw JSON or XML API dump) of all Plex items in the Plex
section with key = viewId.
"""
result = []
url = "{server}/library/sections/%s/all" % viewId
jsondata = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
try:
result = jsondata['_children']
except TypeError:
# Maybe we received an XML, check for that with tag attribute
try:
jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
self.logMsg("Error retrieving all items for Plex section %s"
% viewId, -1)
return result
except KeyError:
self.logMsg("Error retrieving all items for Plex section %s"
% viewId, -1)
return result
def GetAllPlexLeaves(self, viewId, headerOptions={}):
"""
Returns a list (raw JSON or XML API dump) of all Plex subitems for the
key.
(e.g. /library/sections/2/allLeaves pointing to all TV shows)
Input:
viewId Id of Plex library, e.g. '2'
headerOptions to override the download headers
"""
result = []
url = "{server}/library/sections/%s/allLeaves" % viewId
jsondata = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
try:
result = jsondata['_children']
except TypeError:
# Maybe we received an XML, check for that with tag attribute
try:
jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
self.logMsg("Error retrieving all leaves for Plex section %s"
% viewId, -1)
return result
except KeyError:
self.logMsg("Error retrieving all leaves for Plex viewId %s"
% viewId, -1)
return result
def GetAllPlexChildren(self, key):
"""
Returns a list (raw JSON API dump) of all Plex children for the key.
(e.g. /library/metadata/194853/children pointing to a season)
Input:
key Key to a Plex item, e.g. 12345
"""
result = []
url = "{server}/library/metadata/%s/children" % key
jsondata = self.doUtils.downloadUrl(url)
try:
result = jsondata['_children']
except KeyError:
self.logMsg("Error retrieving all children for Plex item %s" % key, -1)
pass
return result
def GetPlexMetadata(self, key):
"""
Returns raw API metadata for key as an etree XML.
Can be called with either Plex key '/library/metadata/xxxx'metadata
OR with the digits 'xxxx' only.
Returns an empty string '' if something went wrong
"""
xml = ''
key = str(key)
if '/library/metadata/' in key:
url = "{server}" + key
else:
url = "{server}/library/metadata/" + key
arguments = {
'checkFiles': 1, # No idea
'includeExtras': 1, # Trailers and Extras => Extras
'includeRelated': 1, # Similar movies => Video -> Related
'includeRelatedCount': 5,
'includeOnDeck': 1,
'includeChapters': 1,
'includePopularLeaves': 1,
'includeConcerts': 1
}
url = url + '?' + urlencode(arguments)
headerOptions = {'Accept': 'application/xml'}
xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
# Did we receive a valid XML?
try:
xml.tag
# Nope we did not receive a valid XML
except AttributeError:
self.logMsg("Error retrieving metadata for %s" % url, -1)
xml = ''
return xml
@utils.logging
class API():

View file

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
from urllib import urlencode
from xbmcaddon import Addon
from downloadutils import DownloadUtils
from utils import logMsg
addonName = Addon().getAddonInfo('name')
title = "%s %s" % (addonName, __name__)
def XbmcItemtypes():
return ['photo', 'video', 'audio']
def PlexItemtypes():
return ['photo', 'video', 'audio']
def PlexLibraryItemtypes():
return ['movie', 'show']
# later add: 'artist', 'photo'
def EmbyItemtypes():
return ['Movie', 'Series', 'Season', 'Episode']
def XbmcPhoto():
return "photo"
def XbmcVideo():
return "video"
def XbmcAudio():
return "audio"
def PlexPhoto():
return "photo"
def PlexVideo():
return "video"
def PlexAudio():
return "music"
def GetPlexMetadata(key):
"""
Returns raw API metadata for key as an etree XML.
Can be called with either Plex key '/library/metadata/xxxx'metadata
OR with the digits 'xxxx' only.
Returns an empty string '' if something went wrong
"""
xml = ''
key = str(key)
if '/library/metadata/' in key:
url = "{server}" + key
else:
url = "{server}/library/metadata/" + key
arguments = {
'checkFiles': 1, # No idea
'includeExtras': 1, # Trailers and Extras => Extras
'includeRelated': 1, # Similar movies => Video -> Related
'includeRelatedCount': 5,
'includeOnDeck': 1,
'includeChapters': 1,
'includePopularLeaves': 1,
'includeConcerts': 1
}
url = url + '?' + urlencode(arguments)
headerOptions = {'Accept': 'application/xml'}
xml = DownloadUtils().downloadUrl(url, headerOptions=headerOptions)
# Did we receive a valid XML?
try:
xml.tag
# Nope we did not receive a valid XML
except AttributeError:
logMsg(title, "Error retrieving metadata for %s" % url, -1)
xml = ''
return xml
def GetAllPlexChildren(key):
"""
Returns a list (raw JSON API dump) of all Plex children for the key.
(e.g. /library/metadata/194853/children pointing to a season)
Input:
key Key to a Plex item, e.g. 12345
"""
result = []
url = "{server}/library/metadata/%s/children" % key
jsondata = DownloadUtils().downloadUrl(url)
try:
result = jsondata['_children']
except KeyError:
logMsg(
title, "Error retrieving all children for Plex item %s" % key, -1)
pass
return result
def GetPlexSectionResults(viewId, headerOptions={}):
"""
Returns a list (raw JSON or XML API dump) of all Plex items in the Plex
section with key = viewId.
"""
result = []
url = "{server}/library/sections/%s/all" % viewId
jsondata = DownloadUtils().downloadUrl(url, headerOptions=headerOptions)
try:
result = jsondata['_children']
except TypeError:
# Maybe we received an XML, check for that with tag attribute
try:
jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
logMsg(title,
"Error retrieving all items for Plex section %s"
% viewId, -1)
return result
except KeyError:
logMsg(title,
"Error retrieving all items for Plex section %s"
% viewId, -1)
return result
def GetAllPlexLeaves(viewId, headerOptions={}):
"""
Returns a list (raw JSON or XML API dump) of all Plex subitems for the
key.
(e.g. /library/sections/2/allLeaves pointing to all TV shows)
Input:
viewId Id of Plex library, e.g. '2'
headerOptions to override the download headers
"""
result = []
url = "{server}/library/sections/%s/allLeaves" % viewId
jsondata = DownloadUtils().downloadUrl(url, headerOptions=headerOptions)
try:
result = jsondata['_children']
except TypeError:
# Maybe we received an XML, check for that with tag attribute
try:
jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
logMsg(title,
"Error retrieving all leaves for Plex section %s"
% viewId, -1)
return result
except KeyError:
logMsg("Error retrieving all leaves for Plex viewId %s" % viewId, -1)
return result
def GetPlexCollections(mediatype):
"""
Input:
mediatype String or list of strings with possible values
'movie', 'show', 'artist', 'photo'
Output:
List with an entry of the form:
{
'name': xxx Plex title for the media section
'type': xxx Plex type: 'movie', 'show', 'artist', 'photo'
'id': xxx Plex unique key for the section (1, 2, 3...)
'uuid': xxx Other unique Plex key, e.g.
74aec9f2-a312-4723-9436-de2ea43843c1
}
Returns an empty list if nothing is found.
"""
collections = []
url = "{server}/library/sections"
jsondata = DownloadUtils().downloadUrl(url)
try:
result = jsondata['_children']
except KeyError:
pass
else:
for item in result:
contentType = item['type']
if contentType in mediatype:
name = item['title']
contentId = item['key']
uuid = item['uuid']
collections.append({
'name': name,
'type': contentType,
'id': str(contentId),
'uuid': uuid
})
return collections

View file

@ -26,6 +26,7 @@ import playutils
import api
import PlexAPI
import PlexFunctions
import embydb_functions
#################################################################################################
@ -61,13 +62,13 @@ def plexCompanion(fullurl, resume=None):
else:
resume = round(float(resume) / 1000.0, 6)
# Start playing
item = PlexAPI.PlexAPI().GetPlexMetadata(itemid)
item = PlexFunctions.GetPlexMetadata(itemid)
pbutils.PlaybackUtils(item).play(itemid, dbid, seektime=resume)
def doPlayback(itemid, dbid):
# Get a first XML to get the librarySectionUUID
item = PlexAPI.PlexAPI().GetPlexMetadata(itemid)
item = PlexFunctions.GetPlexMetadata(itemid)
# Use that to call the playlist
xmlPlaylist = PlexAPI.API(item).GetPlexPlaylist()
if xmlPlaylist:

View file

@ -3,7 +3,7 @@
##################################################################################################
import threading
from datetime import datetime, timedelta
from datetime import datetime
import Queue
import xbmc
@ -22,6 +22,7 @@ import userclient
import videonodes
import PlexAPI
import PlexFunctions
##################################################################################################
@ -47,7 +48,6 @@ class ThreadedGetMetadata(threading.Thread):
threading.Thread.__init__(self)
def run(self):
plx = PlexAPI.PlexAPI()
# cache local variables because it's faster
queue = self.queue
out_queue = self.out_queue
@ -63,7 +63,7 @@ class ThreadedGetMetadata(threading.Thread):
continue
# Download Metadata
try:
plexXML = plx.GetPlexMetadata(updateItem['itemId'])
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
except:
raise
# check whether valid XML
@ -218,7 +218,6 @@ class LibrarySync(threading.Thread):
self.user = userclient.UserClient()
self.emby = embyserver.Read_EmbyServer()
self.vnodes = videonodes.VideoNodes()
self.plx = PlexAPI.PlexAPI()
self.syncThreadNumber = int(utils.settings('syncThreadNumber'))
threading.Thread.__init__(self)
@ -263,37 +262,44 @@ class LibrarySync(threading.Thread):
# Get last sync time
lastSync = utils.window('LastIncrementalSync')
if not lastSync:
lastSync = "2016-01-01T00:00:00Z"
# Original Emby format:
# lastSync = "2016-01-01T00:00:00Z"
# January 1, 2015 at midnight:
lastSync = '1420070400'
self.logMsg("Last sync run: %s" % lastSync, 1)
# Convert time to unix main time or whatever it is called
# Get all PMS views
# Get all PMS views and PMS items already saved in Kodi
self.maintainViews()
embyconn = utils.kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
views = []
for itemtype in PlexAPI.PlexLibraryItemtypes():
for itemtype in PlexFunctions.PlexLibraryItemtypes():
views.append(emby_db.getView_byType(itemtype))
# Also get checksums of Plex items already saved in Kodi
self.allKodiElementsId = {}
for itemtype in PlexAPI.dk():
self.logMsg("views is now: %s" % views, 2)
# Also get checksums of every Plex items already saved in Kodi
allKodiElementsId = {}
for itemtype in PlexFunctions.EmbyItemtypes():
try:
self.allKodiElementsId = dict(emby_db.getChecksum('Movie'))
allKodiElementsId.update(dict(emby_db.getChecksum(itemtype)))
except ValueError:
pass
views = doUtils.downloadUrl("{server}/library/sections")
try:
views = views['_children']
except TypeError:
self.logMsg("Could not process fastSync view json, aborting", 0)
return False
self.logMsg("allKodiElementsId is now: %s" % allKodiElementsId, 2)
# Run through views and get latest changed elements using time diff
for view in views:
pass
if self.threadStopped():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
all_plexmovies = PlexFunctions.GetPlexSectionResults(viewId)
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(all_plexmovies,
itemType,
'add_update',
viewName,
viewId)
# Figure out whether an item needs updating
# Process updates
@ -322,12 +328,8 @@ class LibrarySync(threading.Thread):
def saveLastSync(self):
# Save last sync time
overlap = 2
time_now = datetime.utcnow()-timedelta(minutes=overlap)
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
self.logMsg("New sync time: client time -%s min: %s"
% (overlap, lastSync), 1)
lastSync = str(utils.getUnixTimestamp())
self.logMsg("New sync time: %s" % lastSync, 1)
utils.window('LastIncrementalSync', value=lastSync)
def initializeDBs(self):
@ -728,7 +730,6 @@ class LibrarySync(threading.Thread):
def PlexMovies(self):
# Initialize
plx = PlexAPI.PlexAPI()
self.allPlexElementsId = {}
embyconn = utils.kodiSQL('emby')
@ -761,7 +762,7 @@ class LibrarySync(threading.Thread):
# Get items per view
viewId = view['id']
viewName = view['name']
all_plexmovies = plx.GetPlexSectionResults(viewId)
all_plexmovies = PlexFunctions.GetPlexSectionResults(viewId)
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(all_plexmovies,
itemType,
@ -791,11 +792,10 @@ class LibrarySync(threading.Thread):
This is done by downloading one XML for ALL elements with viewId
"""
starttotal = datetime.now()
plx = PlexAPI.PlexAPI()
# Download XML, not JSON, because PMS JSON seems to be damaged
headerOptions = {'Accept': 'application/xml'}
plexItems = plx.GetAllPlexLeaves(viewId,
headerOptions=headerOptions)
plexItems = PlexFunctions.GetAllPlexLeaves(
viewId, headerOptions=headerOptions)
itemMth = getattr(itemtypes, itemType)
with itemMth() as method:
method.updateUserdata(plexItems)
@ -854,7 +854,6 @@ class LibrarySync(threading.Thread):
def PlexTVShows(self):
# Initialize
plx = PlexAPI.PlexAPI()
self.allPlexElementsId = {}
itemType = 'TVShows'
# Open DB connections
@ -897,7 +896,7 @@ class LibrarySync(threading.Thread):
# Get items per view
viewId = view['id']
viewName = view['name']
allPlexTvShows = plx.GetPlexSectionResults(viewId)
allPlexTvShows = PlexFunctions.GetPlexSectionResults(viewId)
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(allPlexTvShows,
itemType,
@ -915,7 +914,7 @@ class LibrarySync(threading.Thread):
if self.threadStopped():
return False
# Grab all seasons to tvshow from PMS
seasons = plx.GetAllPlexChildren(tvShowId)
seasons = PlexFunctions.GetAllPlexChildren(tvShowId)
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(seasons,
itemType,
@ -931,7 +930,7 @@ class LibrarySync(threading.Thread):
if self.threadStopped():
return False
# Grab all episodes to tvshow from PMS
episodes = plx.GetAllPlexLeaves(view['id'])
episodes = PlexFunctions.GetAllPlexLeaves(view['id'])
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(episodes,
itemType,
@ -948,7 +947,7 @@ class LibrarySync(threading.Thread):
# Cycle through tv shows
with itemtypes.TVShows() as TVshow:
for tvShowId in allPlexTvShowsId:
XMLtvshow = plx.GetPlexMetadata(tvShowId)
XMLtvshow = PlexFunctions.GetPlexMetadata(tvShowId)
TVshow.refreshSeasonEntry(XMLtvshow, tvShowId)
self.logMsg("Season info refreshed", 1)

View file

@ -10,6 +10,8 @@ import time
import unicodedata
import xml.etree.ElementTree as etree
from functools import wraps, update_wrapper
from datetime import datetime, timedelta
from calendar import timegm
import xbmc
import xbmcaddon
@ -114,7 +116,7 @@ def logging(cls):
# Define new class methods and attach them to class
def newFunction(self, msg, lvl=0):
title = "%s %s" % (self.addonName, cls.__name__)
title = "%s %s" % (addonName, cls.__name__)
logMsg(title, msg, lvl)
cls.logMsg = newFunction
@ -122,6 +124,21 @@ def logging(cls):
return cls
def getUnixTimestamp(secondsIntoTheFuture=None):
"""
Returns a Unix time stamp (seconds passed since January 1 1970) for NOW as
an integer.
Optionally, pass secondsIntoTheFuture: positive int's will result in a
future timestamp, negative the past
"""
if secondsIntoTheFuture:
future = datetime.utcnow() + timedelta(seconds=secondsIntoTheFuture)
else:
future = datetime.utcnow()
return timegm(future.timetuple())
def logMsg(title, msg, level=1):
# Get the logLevel set in UserClient