Merge branch 'develop'

This commit is contained in:
tomkat83 2016-03-11 14:48:27 +01:00
commit a360663134
10 changed files with 495 additions and 86 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" <addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect" name="PlexKodiConnect"
version="1.0.7" version="1.0.8"
provider-name="croneter"> provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>

View file

@ -1,3 +1,8 @@
version 1.0.8
- Redesigned fast incremential sync
- Workaround to sync Kodi time with PMS time
- Fix library sync key error
version 1.0.7 version 1.0.7
- Fix userclient - Fix userclient
- Fix KeyError in library sync - Fix KeyError in library sync

View file

@ -1476,36 +1476,6 @@ class API():
""" """
return self.part return self.part
def DateToKodi(self, stamp):
"""
converts a Unix time stamp (seconds passed sinceJanuary 1 1970) to a
propper, human-readable time stamp used by Kodi
Output: Y-m-d h:m:s = 2009-04-05 23:16:04
"""
# DATEFORMAT = xbmc.getRegion('dateshort')
# TIMEFORMAT = xbmc.getRegion('meridiem')
# date_time = time.localtime(stamp)
# if DATEFORMAT[1] == 'd':
# localdate = time.strftime('%d-%m-%Y', date_time)
# elif DATEFORMAT[1] == 'm':
# localdate = time.strftime('%m-%d-%Y', date_time)
# else:
# localdate = time.strftime('%Y-%m-%d', date_time)
# if TIMEFORMAT != '/':
# localtime = time.strftime('%I:%M%p', date_time)
# else:
# localtime = time.strftime('%H:%M', date_time)
# return localtime + ' ' + localdate
try:
# DATEFORMAT = xbmc.getRegion('dateshort')
# TIMEFORMAT = xbmc.getRegion('meridiem')
date_time = time.localtime(float(stamp))
localdate = time.strftime('%Y-%m-%d %H:%M:%S', date_time)
except:
localdate = None
return localdate
def getType(self): def getType(self):
""" """
Returns the type of media, e.g. 'movie' or 'clip' for trailers Returns the type of media, e.g. 'movie' or 'clip' for trailers
@ -1543,7 +1513,7 @@ class API():
""" """
Returns the date when this library item was created Returns the date when this library item was created
""" """
return self.DateToKodi(self.item.attrib.get('addedAt', None)) return utils.DateToKodi(self.item.attrib.get('addedAt', None))
def getUserData(self): def getUserData(self):
""" """
@ -1576,7 +1546,7 @@ class API():
played = True played = True
try: try:
lastPlayedDate = self.DateToKodi(int(item['lastViewedAt'])) lastPlayedDate = utils.DateToKodi(int(item['lastViewedAt']))
except: except:
lastPlayedDate = None lastPlayedDate = None
@ -1851,10 +1821,10 @@ class API():
] ]
""" """
item = self.item.attrib item = self.item.attrib
key = item['grandparentRatingKey'] key = item.get('grandparentRatingKey')
title = item['grandparentTitle'] title = item.get('grandparentTitle')
season = item['parentIndex'] season = item.get('parentIndex')
episode = item['index'] episode = item.get('index')
return key, title, season, episode return key, title, season, episode
def addPlexHeadersToUrl(self, url, arguments={}): def addPlexHeadersToUrl(self, url, arguments={}):

View file

@ -3,6 +3,7 @@ from urllib import urlencode
from ast import literal_eval from ast import literal_eval
from urlparse import urlparse, parse_qs from urlparse import urlparse, parse_qs
import re import re
import time
from xbmcaddon import Addon from xbmcaddon import Addon
@ -371,3 +372,22 @@ def PMSHttpsEnabled(url):
# couldn't get an xml - switch to http traffic # couldn't get an xml - switch to http traffic
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1) logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1)
return False return False
def scrobble(ratingKey, state):
"""
Tells the PMS to set an item's watched state to state="watched" or
state="unwatched"
"""
args = {
'key': ratingKey,
'identifier': 'com.plexapp.plugins.library'
}
if state == "watched":
url = "{server}/:/scrobble?" + urlencode(args)
elif state == "unwatched":
url = "{server}/:/unscrobble?" + urlencode(args)
else:
return
downloadutils.DownloadUtils().downloadUrl(url, type="GET")
logMsg("Toggled watched state for Plex item %s" % ratingKey, 1)

View file

@ -152,6 +152,22 @@ class Embydb_Functions():
)) ))
self.embycursor.execute(query, (viewid,)) self.embycursor.execute(query, (viewid,))
def getItem_byFileId(self, fileId):
"""
Returns the Plex itemId by using the Kodi fileId
"""
query = ' '.join((
"SELECT emby_id",
"FROM emby",
"WHERE kodi_fileid = ?"
))
try:
self.embycursor.execute(query, (fileId,))
item = self.embycursor.fetchone()[0]
return item
except:
return None
def getItem_byId(self, embyid): def getItem_byId(self, embyid):
embycursor = self.embycursor embycursor = self.embycursor
@ -183,6 +199,23 @@ class Embydb_Functions():
return items return items
def getPlexId(self, kodiid):
"""
Returns the Plex ID usind the Kodiid. Result:
(Plex Id, Parent's Plex Id)
"""
query = ' '.join((
"SELECT emby_id, parent_id",
"FROM emby",
"WHERE kodi_id = ?"
))
try:
self.embycursor.execute(query, (kodiid))
item = self.embycursor.fetchone()
return item
except:
return None
def getItem_byKodiId(self, kodiid, mediatype): def getItem_byKodiId(self, kodiid, mediatype):
embycursor = self.embycursor embycursor = self.embycursor

View file

@ -231,10 +231,12 @@ class Items(object):
time=time, time=time,
sound=False) sound=False)
def updateUserdata(self, xml): def updateUserdata(self, xml, viewtag=None, viewid=None):
""" """
Updates the Kodi watched state of the item from PMS. Also retrieves Updates the Kodi watched state of the item from PMS. Also retrieves
Plex resume points for movies in progress. Plex resume points for movies in progress.
viewtag and viewid only serve as dummies
""" """
for mediaitem in xml: for mediaitem in xml:
API = PlexAPI.API(mediaitem) API = PlexAPI.API(mediaitem)
@ -1256,12 +1258,13 @@ class TVShows(Items):
seriesId, seriesName, season, episode = API.getEpisodeDetails() seriesId, seriesName, season, episode = API.getEpisodeDetails()
if season is None: if season is None:
if item.get('AbsoluteEpisodeNumber'): season = -1
# Anime scenario # if item.get('AbsoluteEpisodeNumber'):
season = 1 # # Anime scenario
episode = item['AbsoluteEpisodeNumber'] # season = 1
else: # episode = item['AbsoluteEpisodeNumber']
season = -1 # else:
# season = -1
# Specials ordering within season # Specials ordering within season
if item.get('AirsAfterSeasonNumber'): if item.get('AirsAfterSeasonNumber'):

View file

@ -17,7 +17,7 @@ class GetKodiDB():
do stuff with kodi_db do stuff with kodi_db
Parameters: Parameters:
itemType: itemtype for Kodi DB, e.g. 'video' itemType: itemtype for Kodi DB, e.g. 'video', 'music'
On exiting "with" (no matter what), commits get automatically committed On exiting "with" (no matter what), commits get automatically committed
and the db gets closed and the db gets closed
@ -701,6 +701,68 @@ class Kodidb_Functions():
) )
cursor.execute(query, (fileid, 2, subtitletrack)) cursor.execute(query, (fileid, 2, subtitletrack))
def getResumes(self):
"""
VIDEOS
Returns all Kodi idFile that have a resume point set (not unwatched
ones or items that have already been completely watched)
"""
cursor = self.cursor
query = ' '.join((
"SELECT idFile",
"FROM bookmark"
))
try:
rows = cursor.execute(query)
except:
return []
ids = []
for row in rows:
ids.append(row[0])
return ids
def getUnplayedMusicItems(self):
"""
MUSIC
Returns all Kodi Item idFile that have not yet been completely played
"""
query = ' '.join((
"SELECT idPath",
"FROM song",
"WHERE iTimesPlayed IS NULL OR iTimesPlayed = ''"
))
try:
rows = self.cursor.execute(query)
except:
return []
ids = []
for row in rows:
ids.append(row[0])
return ids
def getUnplayedItems(self):
"""
VIDEOS
Returns all Kodi Item idFile that have not yet been completely played
"""
query = ' '.join((
"SELECT idFile",
"FROM files",
"WHERE playCount IS NULL OR playCount = ''"
))
try:
rows = self.cursor.execute(query)
except:
return []
ids = []
for row in rows:
ids.append(row[0])
return ids
def addPlaystate(self, fileid, resume_seconds, total_seconds, playcount, dateplayed): def addPlaystate(self, fileid, resume_seconds, total_seconds, playcount, dateplayed):
cursor = self.cursor cursor = self.cursor

View file

@ -11,8 +11,7 @@ import downloadutils
import embydb_functions as embydb import embydb_functions as embydb
import playbackutils as pbutils import playbackutils as pbutils
import utils import utils
from PlexFunctions import scrobble
from urllib import urlencode
############################################################################### ###############################################################################
@ -144,16 +143,11 @@ class KodiMonitor(xbmc.Monitor):
utils.window('emby_skipWatched%s' % itemid, clear=True) utils.window('emby_skipWatched%s' % itemid, clear=True)
else: else:
# notify the server # notify the server
args = {'key': itemid,
'identifier': 'com.plexapp.plugins.library'}
if playcount != 0: if playcount != 0:
url = "{server}/:/scrobble?" + urlencode(args) scrobble(itemid, 'watched')
doUtils.downloadUrl(url, type="GET")
self.logMsg("Mark as watched for itemid: %s" % itemid, 1)
else: else:
url = "{server}/:/unscrobble?" + urlencode(args) scrobble(itemid, 'unwatched')
doUtils.downloadUrl(url, type="GET")
self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1)
finally: finally:
embycursor.close() embycursor.close()

View file

@ -4,6 +4,10 @@
from threading import Thread, Lock from threading import Thread, Lock
import Queue import Queue
try:
import xml.etree.cElementTree as etree
except ImportError:
import xml.etree.ElementTree as etree
import xbmc import xbmc
import xbmcgui import xbmcgui
@ -234,6 +238,12 @@ class LibrarySync(Thread):
self.enableBackgroundSync = True if utils.settings( self.enableBackgroundSync = True if utils.settings(
'enableBackgroundSync') == "true" else False 'enableBackgroundSync') == "true" else False
# Time offset between Kodi and PMS in seconds (=Koditime - PMStime)
self.timeoffset = 0
# Time in seconds to look into the past when looking for PMS changes
# (safety margin - the larger, the more items we need to process)
self.syncPast = 30
Thread.__init__(self) Thread.__init__(self)
def showKodiNote(self, message, forced=False, icon="plex"): def showKodiNote(self, message, forced=False, icon="plex"):
@ -261,6 +271,180 @@ class LibrarySync(Thread):
time=7000, time=7000,
sound=True) sound=True)
def syncPMStime(self):
"""
PMS does not provide a means to get a server timestamp. This is a work-
around.
"""
self.logMsg('Synching time with PMS server', 0)
# Find a PMS item where we can toggle the view state to enforce a
# change in lastViewedAt
with kodidb.GetKodiDB('video') as kodi_db:
unplayedIds = kodi_db.getUnplayedItems()
resumeIds = kodi_db.getResumes()
self.logMsg('resumeIds: %s' % resumeIds, 1)
plexId = False
for unplayedId in unplayedIds:
if unplayedId not in resumeIds:
# Found an item we can work with!
kodiId = unplayedId
self.logMsg('Found kodiId: %s' % kodiId, 1)
# Get Plex ID using the Kodi ID
with embydb.GetEmbyDB() as emby_db:
plexId = emby_db.getItem_byFileId(kodiId)
if plexId:
self.logMsg('Found plexId: %s' % plexId, 1)
break
# Try getting a music item if we did not find a video item
if not plexId:
self.logMsg("Could not find a video item to sync time with", 0)
with kodidb.GetKodiDB('music') as kodi_db:
unplayedIds = kodi_db.getUnplayedMusicItems()
# We don't care about resuming songs in the middle
for unplayedId in unplayedIds:
# Found an item we can work with!
kodiId = unplayedId
self.logMsg('Found kodiId: %s' % kodiId, 1)
# Get Plex ID using the Kodi ID
with embydb.GetEmbyDB() as emby_db:
plexId = emby_db.getItem_byFileId(kodiId)
if plexId:
self.logMsg('Found plexId: %s' % plexId, 1)
break
else:
self.logMsg("Could not find an item to sync time with", -1)
self.logMsg("Aborting PMS-Kodi time sync", -1)
return
# Get the Plex item's metadata
xml = PlexFunctions.GetPlexMetadata(plexId)
if not xml:
self.logMsg("Could not download metadata, aborting time sync", -1)
return
libraryId = xml[0].attrib['librarySectionID']
# Get a PMS timestamp to start our question with
timestamp = xml[0].attrib.get('lastViewedAt')
if not timestamp:
timestamp = xml[0].attrib.get('updatedAt')
self.logMsg('Using items updatedAt=%s' % timestamp, 1)
if not timestamp:
timestamp = xml[0].attrib.get('addedAt')
self.logMsg('Using items addedAt=%s' % timestamp, 1)
# Set the timer
koditime = utils.getUnixTimestamp()
# Toggle watched state
PlexFunctions.scrobble(plexId, 'watched')
# Let the PMS process this first!
xbmc.sleep(2000)
# Get all PMS items to find the item we changed
items = PlexFunctions.GetAllPlexLeaves(libraryId,
lastViewedAt=timestamp)
# Toggle watched state back
PlexFunctions.scrobble(plexId, 'unwatched')
# Get server timestamp for this change
plextime = None
for item in items:
if item.attrib['ratingKey'] == plexId:
plextime = item.attrib.get('lastViewedAt')
break
if not plextime:
self.logMsg("Could not set the items watched state, abort", -1)
return
# Calculate time offset Kodi-PMS
self.timeoffset = int(koditime) - int(plextime)
self.logMsg("Time offset Koditime - PMStime in seconds: %s"
% str(self.timeoffset), 0)
def getPMSfromKodiTime(self, koditime):
"""
Uses self.timeoffset to return the PMS time for a given Kodi timestamp
(in unix time)
Feed with integers
"""
return koditime - self.timeoffset
def resetProcessedItems(self):
"""
Resets the list of PMS items that we have already processed
"""
self.processed = {
'movie': {},
'show': {},
'season': {},
'episode': {},
'artist': {},
'album': {},
'track': {}
}
def getFastUpdateList(self, xml, plexType, viewName, viewId):
"""
THIS METHOD NEEDS TO BE FAST! => e.g. no API calls
Adds items to self.updatelist as well as self.allPlexElementsId dict
Input:
xml: PMS answer for section items
plexType: 'movie', 'show', 'episode', ...
viewName: Name of the Plex view (e.g. 'My TV shows')
viewId: Id/Key of Plex library (e.g. '1')
Output: self.updatelist, self.allPlexElementsId
self.updatelist APPENDED(!!) list itemids (Plex Keys as
as received from API.getRatingKey())
One item in this list is of the form:
'itemId': xxx,
'itemType': 'Movies','TVShows', ...
'method': 'add_update', 'add_updateSeason', ...
'viewName': xxx,
'viewId': xxx,
'title': xxx
self.allPlexElementsId APPENDED(!!) dict
= {itemid: checksum}
"""
# Needs to call other methods than if we're only updating userdata
for item in xml:
itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey'
if not itemId:
continue
lastViewedAt = item.attrib.get('lastViewedAt')
updatedAt = item.attrib.get('updatedAt')
# returns the tuple (lastViewedAt, updatedAt) for the
# specific item
res = self.processed[plexType].get(itemId)
if res:
# Only look at the updatedAt flag!
# tuple: (lastViewedAt, updatedAt)
if res[1] == updatedAt:
# Nothing to update, we have already processed this
# item
continue
title = item.attrib.get('title', 'Missing Title Name')
# We need to process this:
self.updatelist.append({
'itemId': itemId,
'itemType': PlexFunctions.GetItemClassFromType(
plexType),
'method': PlexFunctions.GetMethodFromPlexType(plexType),
'viewName': viewName,
'viewId': viewId,
'title': title
})
# And safe to self.processed:
self.processed[plexType][itemId] = (lastViewedAt, updatedAt)
# Quickly log
if self.updatelist:
self.logMsg('fastSync updatelist: %s' % self.updatelist, 1)
self.logMsg('fastSync processed list: %s' % self.processed, 1)
def fastSync(self): def fastSync(self):
""" """
Fast incremential lib sync Fast incremential lib sync
@ -271,24 +455,24 @@ class LibrarySync(Thread):
This will NOT remove items from Kodi db that were removed from the PMS This will NOT remove items from Kodi db that were removed from the PMS
(happens only during fullsync) (happens only during fullsync)
Currently, ALL items returned by the PMS (because they've just been Items that are processed are appended to the dict self.processed:
edited by the PMS or have been watched) will be processed. This will {
probably happen several times. '<Plex itemtype>': e.g. 'movie'
{
'<ratingKey>': ( unique plex id 'ratingKey' as str
lastViewedAt,
updatedAt
)
}
}
""" """
self.compare = True # Get last sync time and look a bit in the past (safety margin)
# Get last sync time
lastSync = self.lastSync - self.syncPast lastSync = self.lastSync - self.syncPast
if not lastSync:
# Original Emby format:
# lastSync = "2016-01-01T00:00:00Z"
# January 1, 2015 at midnight:
lastSync = 1420070400
# Set new timestamp NOW because sync might take a while # Set new timestamp NOW because sync might take a while
self.saveLastSync() self.saveLastSync()
# Original idea: Get all PMS items already saved in Kodi # Original idea: Get all PMS items already saved in Kodi
# Also get checksums of every Plex items already saved in Kodi # Also get checksums of every Plex items already saved in Kodi
# NEW idea: process every item returned by the PMS
self.allKodiElementsId = {} self.allKodiElementsId = {}
# Run through views and get latest changed elements using time diff # Run through views and get latest changed elements using time diff
@ -296,39 +480,94 @@ class LibrarySync(Thread):
self.updateKodiMusicLib = False self.updateKodiMusicLib = False
for view in self.views: for view in self.views:
self.updatelist = [] self.updatelist = []
if self.threadStopped():
return True
# Get items per view # Get items per view
items = PlexFunctions.GetAllPlexLeaves(view['id'], items = PlexFunctions.GetAllPlexLeaves(
updatedAt=lastSync) view['id'], updatedAt=self.getPMSfromKodiTime(lastSync))
# Just skip item if something went wrong # Just skip if something went wrong
if not items: if not items:
continue continue
# Get one itemtype, because they're the same in the PMS section # Get one itemtype, because they're the same in the PMS section
plexType = items[0].attrib['type'] try:
plexType = items[0].attrib['type']
except:
# There was no child - PMS response is empty
continue
# Populate self.updatelist # Populate self.updatelist
self.GetUpdatelist(items, self.getFastUpdateList(
PlexFunctions.GetItemClassFromType(plexType), items, plexType, view['name'], view['id'])
PlexFunctions.GetMethodFromPlexType(plexType),
view['name'],
view['id'])
# Process self.updatelist # Process self.updatelist
if self.updatelist: if self.updatelist:
if self.updatelist[0]['itemType'] in ['Movies', 'TVShows']: if self.updatelist[0]['itemType'] in ['Movies', 'TVShows']:
self.updateKodiVideoLib = True self.updateKodiVideoLib = True
elif self.updatelist[0]['itemType'] == 'Music': elif self.updatelist[0]['itemType'] == 'Music':
self.updateKodiMusicLib = True self.updateKodiMusicLib = True
# Do the work
self.GetAndProcessXMLs( self.GetAndProcessXMLs(
PlexFunctions.GetItemClassFromType(plexType), PlexFunctions.GetItemClassFromType(plexType),
showProgress=False) showProgress=False)
self.updatelist = []
# Update userdata self.updatelist = []
# Update userdata DIRECTLY
# We don't need to refresh the Kodi library for deltas!!
# Start with an empty ElementTree and attach items to update
movieupdate = False
episodeupdate = False
songupdate = False
for view in self.views: for view in self.views:
self.PlexUpdateWatched( items = PlexFunctions.GetAllPlexLeaves(
view['id'], view['id'], lastViewedAt=self.getPMSfromKodiTime(lastSync))
PlexFunctions.GetItemClassFromType(view['itemtype']), for item in items:
lastViewedAt=lastSync) itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey'
if not itemId:
continue
plexType = item.attrib['type']
lastViewedAt = item.attrib.get('lastViewedAt')
updatedAt = item.attrib.get('updatedAt')
# returns the tuple (lastViewedAt, updatedAt) for the
# specific item
res = self.processed[plexType].get(itemId)
if res:
# Only look at lastViewedAt
if res[0] == lastViewedAt:
# Nothing to update, we have already processed this
# item
continue
if plexType == 'movie':
movieupdate = True
try:
movieXML.append(item)
except:
movieXML = etree.Element('root')
movieXML.append(item)
elif plexType == 'episode':
episodeupdate = True
try:
episodeXML.append(item)
except:
episodeXML = etree.Element('root')
episodeXML.append(item)
elif plexType == 'track':
songupdate = True
try:
musicXML.append(item)
except:
musicXML = etree.Element('root')
musicXML.append(item)
# And safe to self.processed:
self.processed[plexType][itemId] = (lastViewedAt, updatedAt)
if movieupdate:
with itemtypes.Movies() as movies:
movies.updateUserdata(movieXML)
if episodeupdate:
with itemtypes.TVShows() as tvshows:
tvshows.updateUserdata(episodeXML)
if songupdate:
with itemtypes.Music() as music:
music.updateUserdata(musicXML)
# Let Kodi update the library now (artwork and userdata) # Let Kodi update the library now (artwork and userdata)
if self.updateKodiVideoLib: if self.updateKodiVideoLib:
@ -338,8 +577,6 @@ class LibrarySync(Thread):
self.logMsg("Doing Kodi Music Lib update", 1) self.logMsg("Doing Kodi Music Lib update", 1)
xbmc.executebuiltin('UpdateLibrary(music)') xbmc.executebuiltin('UpdateLibrary(music)')
# Reset and return
self.allPlexElementsId = {}
# Show warning if itemtypes.py crashed at some point # Show warning if itemtypes.py crashed at some point
if utils.window('plex_scancrashed') == 'true': if utils.window('plex_scancrashed') == 'true':
xbmcgui.Dialog().ok(self.addonName, self.__language__(39408)) xbmcgui.Dialog().ok(self.addonName, self.__language__(39408))
@ -1168,6 +1405,9 @@ class LibrarySync(Thread):
count = 0 count = 0
errorcount = 0 errorcount = 0
# Initialize self.processed
self.resetProcessedItems()
log("---===### Starting LibrarySync ###===---", 0) log("---===### Starting LibrarySync ###===---", 0)
while not threadStopped(): while not threadStopped():
@ -1223,6 +1463,8 @@ class LibrarySync(Thread):
log("Db version: %s" % settings('dbCreatedWithVersion'), 0) log("Db version: %s" % settings('dbCreatedWithVersion'), 0)
log("Initial start-up full sync starting", 0) log("Initial start-up full sync starting", 0)
librarySync = fullSync(manualrun=True) librarySync = fullSync(manualrun=True)
# Initialize time offset Kodi - PMS
self.syncPMStime()
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
if librarySync: if librarySync:
log("Initial start-up full sync successful", 0) log("Initial start-up full sync successful", 0)
@ -1282,6 +1524,10 @@ class LibrarySync(Thread):
# Run full lib scan approx every 30min # Run full lib scan approx every 30min
if count >= 1800: if count >= 1800:
count = 0 count = 0
# Also reset self.processed, just in case
self.resetProcessedItems()
# Recalculate time offset Kodi - PMS
self.syncPMStime()
window('emby_dbScan', value="true") window('emby_dbScan', value="true")
log('Running background full lib scan', 0) log('Running background full lib scan', 0)
fullSync(manualrun=True) fullSync(manualrun=True)

View file

@ -19,12 +19,88 @@ import xbmcaddon
import xbmcgui import xbmcgui
import xbmcvfs import xbmcvfs
############################################################################### ###############################################################################
addonName = xbmcaddon.Addon().getAddonInfo('name') addonName = xbmcaddon.Addon().getAddonInfo('name')
def DateToKodi(stamp):
"""
converts a Unix time stamp (seconds passed sinceJanuary 1 1970) to a
propper, human-readable time stamp used by Kodi
Output: Y-m-d h:m:s = 2009-04-05 23:16:04
"""
# DATEFORMAT = xbmc.getRegion('dateshort')
# TIMEFORMAT = xbmc.getRegion('meridiem')
# date_time = time.localtime(stamp)
# if DATEFORMAT[1] == 'd':
# localdate = time.strftime('%d-%m-%Y', date_time)
# elif DATEFORMAT[1] == 'm':
# localdate = time.strftime('%m-%d-%Y', date_time)
# else:
# localdate = time.strftime('%Y-%m-%d', date_time)
# if TIMEFORMAT != '/':
# localtime = time.strftime('%I:%M%p', date_time)
# else:
# localtime = time.strftime('%H:%M', date_time)
# return localtime + ' ' + localdate
try:
# DATEFORMAT = xbmc.getRegion('dateshort')
# TIMEFORMAT = xbmc.getRegion('meridiem')
date_time = time.localtime(float(stamp))
localdate = time.strftime('%Y-%m-%d %H:%M:%S', date_time)
except:
localdate = None
return localdate
def changePlayState(itemType, kodiId, playCount, lastplayed):
"""
YET UNUSED
kodiId: int or str
playCount: int or str
lastplayed: str or int unix timestamp
"""
logMsg("changePlayState", "start", 1)
lastplayed = DateToKodi(lastplayed)
kodiId = int(kodiId)
playCount = int(playCount)
method = {
'movie': ' VideoLibrary.SetMovieDetails',
'episode': 'VideoLibrary.SetEpisodeDetails',
'musicvideo': ' VideoLibrary.SetMusicVideoDetails', # TODO
'show': 'VideoLibrary.SetTVShowDetails', # TODO
'': 'AudioLibrary.SetAlbumDetails', # TODO
'': 'AudioLibrary.SetArtistDetails', # TODO
'track': 'AudioLibrary.SetSongDetails'
}
params = {
'movie': {
'movieid': kodiId,
'playcount': playCount,
'lastplayed': lastplayed
},
'episode': {
'episodeid': kodiId,
'playcount': playCount,
'lastplayed': lastplayed
}
}
query = {
"jsonrpc": "2.0",
"id": 1,
}
query['method'] = method[itemType]
query['params'] = params[itemType]
result = xbmc.executeJSONRPC(json.dumps(query))
result = json.loads(result)
result = result.get('result')
logMsg("changePlayState", "JSON result was: %s" % result, 1)
def IfExists(path): def IfExists(path):
""" """
Kodi's xbmcvfs.exists is broken - it caches the results for directories. Kodi's xbmcvfs.exists is broken - it caches the results for directories.