parent
dea8e6d5f5
commit
6cb69ada3f
3 changed files with 92 additions and 97 deletions
|
@ -149,37 +149,26 @@ class Items(object):
|
|||
db_item[4],
|
||||
userdata['UserRating'])
|
||||
|
||||
def updatePlaystate(self, item):
|
||||
def updatePlaystate(self, mark_played, view_count, resume, duration,
|
||||
file_id, lastViewedAt):
|
||||
"""
|
||||
Use with websockets, not xml
|
||||
"""
|
||||
# If the playback was stopped, check whether we need to increment the
|
||||
# playcount. PMS won't tell us the playcount via websockets
|
||||
if item['state'] in ('stopped', 'ended'):
|
||||
|
||||
# If offset exceeds duration skip update
|
||||
if item['viewOffset'] > item['duration']:
|
||||
log.error("Error while updating play state, viewOffset "
|
||||
"exceeded duration")
|
||||
return
|
||||
|
||||
complete = float(item['viewOffset']) / float(item['duration'])
|
||||
log.info('Item %s stopped with completion rate %s percent.'
|
||||
'Mark item played at %s percent.'
|
||||
% (item['ratingKey'], str(complete), v.MARK_PLAYED_AT), 1)
|
||||
if complete >= v.MARK_PLAYED_AT:
|
||||
log.info('Marking as completely watched in Kodi')
|
||||
try:
|
||||
item['viewCount'] += 1
|
||||
except TypeError:
|
||||
item['viewCount'] = 1
|
||||
item['viewOffset'] = 0
|
||||
if mark_played:
|
||||
log.info('Marking as completely watched in Kodi')
|
||||
try:
|
||||
view_count += 1
|
||||
except TypeError:
|
||||
view_count = 1
|
||||
resume = 0
|
||||
# Do the actual update
|
||||
self.kodi_db.addPlaystate(item['file_id'],
|
||||
item['viewOffset'],
|
||||
item['duration'],
|
||||
item['viewCount'],
|
||||
item['lastViewedAt'])
|
||||
self.kodi_db.addPlaystate(file_id,
|
||||
resume,
|
||||
duration,
|
||||
view_count,
|
||||
lastViewedAt)
|
||||
|
||||
|
||||
class Movies(Items):
|
||||
|
|
|
@ -45,7 +45,7 @@ class LibrarySync(Thread):
|
|||
"""
|
||||
def __init__(self):
|
||||
self.itemsToProcess = []
|
||||
self.sessionKeys = []
|
||||
self.sessionKeys = {}
|
||||
self.fanartqueue = Queue.Queue()
|
||||
if settings('FanartTV') == 'true':
|
||||
self.fanartthread = Process_Fanart_Thread(self.fanartqueue)
|
||||
|
@ -223,6 +223,8 @@ class LibrarySync(Thread):
|
|||
"""
|
||||
repair=True: force sync EVERY item
|
||||
"""
|
||||
# Reset our keys
|
||||
self.sessionKeys = {}
|
||||
# self.compare == False: we're syncing EVERY item
|
||||
# True: we're syncing only the delta, e.g. different checksum
|
||||
self.compare = not repair
|
||||
|
@ -1283,110 +1285,108 @@ class LibrarySync(Thread):
|
|||
Someone (not necessarily the user signed in) is playing something some-
|
||||
where
|
||||
"""
|
||||
items = []
|
||||
for item in data:
|
||||
# Drop buffering messages immediately
|
||||
status = item['state']
|
||||
if status == 'buffering':
|
||||
# Drop buffering messages immediately
|
||||
continue
|
||||
ratingKey = str(item['ratingKey'])
|
||||
plex_id = str(item['ratingKey'])
|
||||
for pid in (0, 1, 2):
|
||||
if ratingKey == state.PLAYER_STATES[pid]['plex_id']:
|
||||
if plex_id == state.PLAYER_STATES[pid]['plex_id']:
|
||||
# Kodi is playing this item - no need to set the playstate
|
||||
continue
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
kodi_info = plex_db.getItem_byId(ratingKey)
|
||||
if kodi_info is None:
|
||||
# Item not (yet) in Kodi library
|
||||
continue
|
||||
sessionKey = item['sessionKey']
|
||||
# Do we already have a sessionKey stored?
|
||||
if sessionKey not in self.sessionKeys:
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
kodi_info = plex_db.getItem_byId(plex_id)
|
||||
if kodi_info is None:
|
||||
# Item not (yet) in Kodi library
|
||||
continue
|
||||
if settings('plex_serverowned') == 'false':
|
||||
# Not our PMS, we are not authorized to get the
|
||||
# sessions
|
||||
# Not our PMS, we are not authorized to get the sessions
|
||||
# On the bright side, it must be us playing :-)
|
||||
self.sessionKeys = {
|
||||
sessionKey: {}
|
||||
}
|
||||
self.sessionKeys[sessionKey] = {}
|
||||
else:
|
||||
# PMS is ours - get all current sessions
|
||||
self.sessionKeys = GetPMSStatus(state.PLEX_TOKEN)
|
||||
log.debug('Updated current sessions. They are: %s'
|
||||
% self.sessionKeys)
|
||||
self.sessionKeys.update(GetPMSStatus(state.PLEX_TOKEN))
|
||||
log.debug('Updated current sessions. They are: %s',
|
||||
self.sessionKeys)
|
||||
if sessionKey not in self.sessionKeys:
|
||||
log.warn('Session key %s still unknown! Skip '
|
||||
'item' % sessionKey)
|
||||
log.info('Session key %s still unknown! Skip '
|
||||
'playstate update', sessionKey)
|
||||
continue
|
||||
|
||||
currSess = self.sessionKeys[sessionKey]
|
||||
# Attach Kodi info to the session
|
||||
self.sessionKeys[sessionKey]['kodi_id'] = kodi_info[0]
|
||||
self.sessionKeys[sessionKey]['file_id'] = kodi_info[1]
|
||||
self.sessionKeys[sessionKey]['kodi_type'] = kodi_info[4]
|
||||
session = self.sessionKeys[sessionKey]
|
||||
if settings('plex_serverowned') != 'false':
|
||||
# Identify the user - same one as signed on with PKC? Skip
|
||||
# update if neither session's username nor userid match
|
||||
# (Owner sometime's returns id '1', not always)
|
||||
if (not state.PLEX_TOKEN and currSess['userId'] == '1'):
|
||||
if not state.PLEX_TOKEN and session['userId'] == '1':
|
||||
# PKC not signed in to plex.tv. Plus owner of PMS is
|
||||
# playing (the '1').
|
||||
# Hence must be us (since several users require plex.tv
|
||||
# token for PKC)
|
||||
pass
|
||||
elif not (currSess['userId'] == state.PLEX_USER_ID
|
||||
or
|
||||
currSess['username'] == state.PLEX_USERNAME):
|
||||
elif not (session['userId'] == state.PLEX_USER_ID or
|
||||
session['username'] == state.PLEX_USERNAME):
|
||||
log.debug('Our username %s, userid %s did not match '
|
||||
'the session username %s with userid %s'
|
||||
% (state.PLEX_USERNAME,
|
||||
state.PLEX_USER_ID,
|
||||
currSess['username'],
|
||||
currSess['userId']))
|
||||
'the session username %s with userid %s',
|
||||
state.PLEX_USERNAME,
|
||||
state.PLEX_USER_ID,
|
||||
session['username'],
|
||||
session['userId'])
|
||||
continue
|
||||
|
||||
# Get an up-to-date XML from the PMS
|
||||
# because PMS will NOT directly tell us:
|
||||
# duration of item
|
||||
# viewCount
|
||||
if currSess.get('duration') is None:
|
||||
xml = GetPlexMetadata(ratingKey)
|
||||
# Get an up-to-date XML from the PMS because PMS will NOT directly
|
||||
# tell us: duration of item viewCount
|
||||
if session.get('duration') is None:
|
||||
xml = GetPlexMetadata(plex_id)
|
||||
if xml in (None, 401):
|
||||
log.error('Could not get up-to-date xml for item %s'
|
||||
% ratingKey)
|
||||
log.error('Could not get up-to-date xml for item %s',
|
||||
plex_id)
|
||||
continue
|
||||
API = PlexAPI.API(xml[0])
|
||||
userdata = API.getUserData()
|
||||
currSess['duration'] = userdata['Runtime']
|
||||
currSess['viewCount'] = userdata['PlayCount']
|
||||
api = PlexAPI.API(xml[0])
|
||||
userdata = api.getUserData()
|
||||
session['duration'] = userdata['Runtime']
|
||||
session['viewCount'] = userdata['PlayCount']
|
||||
# Sometimes, Plex tells us resume points in milliseconds and
|
||||
# not in seconds - thank you very much!
|
||||
if item.get('viewOffset') > currSess['duration']:
|
||||
resume = item.get('viewOffset') / 1000
|
||||
if item['viewOffset'] > session['duration']:
|
||||
resume = item['viewOffset'] / 1000
|
||||
else:
|
||||
resume = item.get('viewOffset')
|
||||
if resume >= v.MARK_PLAYED_AT and status not in ('stopped', 'ended'):
|
||||
# We need to drop these as we'll otherwise NOT mark an item as
|
||||
# completely watched after having seen >90%
|
||||
resume = item['viewOffset']
|
||||
if resume < v.IGNORE_SECONDS_AT_START:
|
||||
continue
|
||||
# Append to list that we need to process
|
||||
items.append({
|
||||
'ratingKey': ratingKey,
|
||||
'kodi_id': kodi_info[0],
|
||||
'file_id': kodi_info[1],
|
||||
'kodi_type': kodi_info[4],
|
||||
'viewOffset': resume,
|
||||
'state': status,
|
||||
'duration': currSess['duration'],
|
||||
'viewCount': currSess['viewCount'],
|
||||
'lastViewedAt': DateToKodi(getUnixTimestamp())
|
||||
})
|
||||
log.debug('Update playstate for user %s with id %s: %s'
|
||||
% (state.PLEX_USERNAME,
|
||||
state.PLEX_USER_ID,
|
||||
items[-1]))
|
||||
# Now tell Kodi where we are
|
||||
for item in items:
|
||||
itemFkt = getattr(itemtypes,
|
||||
v.ITEMTYPE_FROM_KODITYPE[item['kodi_type']])
|
||||
with itemFkt() as Fkt:
|
||||
Fkt.updatePlaystate(item)
|
||||
try:
|
||||
completed = float(resume) / float(session['duration'])
|
||||
except (ZeroDivisionError, TypeError):
|
||||
log.error('Could not mark playstate for %s and session %s',
|
||||
data, session)
|
||||
continue
|
||||
if completed >= v.MARK_PLAYED_AT:
|
||||
# Only mark completely watched ONCE
|
||||
if session.get('marked_played') is None:
|
||||
session['marked_played'] = True
|
||||
mark_played = True
|
||||
else:
|
||||
# Don't mark it as completely watched again
|
||||
continue
|
||||
else:
|
||||
mark_played = False
|
||||
log.debug('Update playstate for user %s with id %s for plex id %s',
|
||||
state.PLEX_USERNAME, state.PLEX_USER_ID, plex_id)
|
||||
item_fkt = getattr(itemtypes,
|
||||
v.ITEMTYPE_FROM_KODITYPE[session['kodi_type']])
|
||||
with item_fkt() as fkt:
|
||||
fkt.updatePlaystate(mark_played,
|
||||
session['viewCount'],
|
||||
resume,
|
||||
session['duration'],
|
||||
session['file_id'],
|
||||
DateToKodi(getUnixTimestamp()))
|
||||
|
||||
def fanartSync(self, refresh=False):
|
||||
"""
|
||||
|
|
|
@ -21,7 +21,13 @@ def tryDecode(string, encoding='utf-8'):
|
|||
string = string.decode()
|
||||
return string
|
||||
|
||||
|
||||
# Percent of playback progress for watching item as partially watched. Anything
|
||||
# more and item will NOT be marked as partially, but fully watched
|
||||
MARK_PLAYED_AT = 0.9
|
||||
# How many seconds of playback do we ignore before marking an item as partially
|
||||
# watched?
|
||||
IGNORE_SECONDS_AT_START = 60
|
||||
|
||||
_ADDON = Addon()
|
||||
ADDON_NAME = 'PlexKodiConnect'
|
||||
|
|
Loading…
Reference in a new issue