Fix episode widget resume not working (add-on paths)
- Hack alert, really... - Need to reset the Kodi DB
This commit is contained in:
parent
ac7b7bb96d
commit
5c33f3c02a
8 changed files with 97 additions and 42 deletions
|
@ -99,14 +99,15 @@ class API(object):
|
||||||
"""
|
"""
|
||||||
return self.item.get('ratingKey')
|
return self.item.get('ratingKey')
|
||||||
|
|
||||||
def path(self, force_first_media=True):
|
def path(self, force_first_media=True, force_addon=False):
|
||||||
"""
|
"""
|
||||||
Returns a "fully qualified path": add-on paths or direct paths
|
Returns a "fully qualified path": add-on paths or direct paths
|
||||||
depending on the current settings. Will NOT valide the playurl
|
depending on the current settings. Will NOT valide the playurl
|
||||||
Returns unicode or None if something went wrong.
|
Returns unicode or None if something went wrong.
|
||||||
"""
|
"""
|
||||||
filename = self.file_path(force_first_media=force_first_media)
|
filename = self.file_path(force_first_media=force_first_media)
|
||||||
if not state.DIRECT_PATHS or self.plex_type() == v.PLEX_TYPE_CLIP:
|
if (not state.DIRECT_PATHS or force_addon
|
||||||
|
or self.plex_type() == v.PLEX_TYPE_CLIP):
|
||||||
if filename and '/' in filename:
|
if filename and '/' in filename:
|
||||||
filename = filename.rsplit('/', 1)
|
filename = filename.rsplit('/', 1)
|
||||||
elif filename:
|
elif filename:
|
||||||
|
@ -116,11 +117,14 @@ class API(object):
|
||||||
except (TypeError, IndexError):
|
except (TypeError, IndexError):
|
||||||
filename = None
|
filename = None
|
||||||
# Set plugin path and media flags using real filename
|
# Set plugin path and media flags using real filename
|
||||||
path = ('plugin://%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
if self.plex_type() == v.PLEX_TYPE_EPISODE:
|
||||||
% (v.ADDON_TYPE[self.plex_type()],
|
# need to include the plex show id in the path
|
||||||
self.plex_id(),
|
path = ('plugin://plugin.video.plexkodiconnect.tvshows/%s/'
|
||||||
self.plex_type(),
|
% self.grandparent_id())
|
||||||
filename))
|
else:
|
||||||
|
path = 'plugin://%s/' % v.ADDON_TYPE[self.plex_type()]
|
||||||
|
path = ('%s?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
|
% (path, self.plex_id(), self.plex_type(), filename))
|
||||||
else:
|
else:
|
||||||
# Direct paths is set the Kodi way
|
# Direct paths is set the Kodi way
|
||||||
path = self.validate_playurl(filename,
|
path = self.validate_playurl(filename,
|
||||||
|
|
|
@ -155,10 +155,7 @@ class ContextMenu(object):
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_KODI_TYPE[self.kodi_type])
|
v.KODI_PLAYLIST_TYPE_FROM_KODI_TYPE[self.kodi_type])
|
||||||
playqueue.clear()
|
playqueue.clear()
|
||||||
state.CONTEXT_MENU_PLAY = True
|
state.CONTEXT_MENU_PLAY = True
|
||||||
handle = ('plugin://%s/?plex_id=%s&plex_type=%s&mode=play'
|
handle = self.api.path(force_first_media=False, force_addon=True)
|
||||||
% (v.ADDON_TYPE[self.plex_type],
|
|
||||||
self.plex_id,
|
|
||||||
self.plex_type))
|
|
||||||
xbmc.executebuiltin('RunPlugin(%s)' % handle)
|
xbmc.executebuiltin('RunPlugin(%s)' % handle)
|
||||||
|
|
||||||
def _extras(self):
|
def _extras(self):
|
||||||
|
|
|
@ -552,14 +552,7 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
||||||
listitem = api.create_listitem(
|
listitem = api.create_listitem(
|
||||||
append_show_title=append_show_title,
|
append_show_title=append_show_title,
|
||||||
append_sxxexx=append_sxxexx)
|
append_sxxexx=append_sxxexx)
|
||||||
if directpaths:
|
url = api.path()
|
||||||
url = api.file_path(force_first_media=True)
|
|
||||||
else:
|
|
||||||
url = ('plugin://%s.tvshows/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
|
||||||
% (v.ADDON_ID,
|
|
||||||
api.plex_id(),
|
|
||||||
api.plex_type(),
|
|
||||||
api.file_name(force_first_media=True)))
|
|
||||||
if api.resume_point():
|
if api.resume_point():
|
||||||
listitem.setProperty('resumetime', str(api.resume_point()))
|
listitem.setProperty('resumetime', str(api.resume_point()))
|
||||||
xbmcplugin.addDirectoryItem(
|
xbmcplugin.addDirectoryItem(
|
||||||
|
@ -829,11 +822,7 @@ def __build_item(xml_element):
|
||||||
elif api.plex_type() == v.PLEX_TYPE_PHOTO:
|
elif api.plex_type() == v.PLEX_TYPE_PHOTO:
|
||||||
url = api.get_picture_path()
|
url = api.get_picture_path()
|
||||||
else:
|
else:
|
||||||
url = 'plugin://%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s' \
|
url = api.path()
|
||||||
% (v.ADDON_TYPE[api.plex_type()],
|
|
||||||
api.plex_id(),
|
|
||||||
api.plex_type(),
|
|
||||||
api.file_name(force_first_media=True))
|
|
||||||
if api.resume_point():
|
if api.resume_point():
|
||||||
listitem.setProperty('resumetime', str(api.resume_point()))
|
listitem.setProperty('resumetime', str(api.resume_point()))
|
||||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||||
|
|
|
@ -147,21 +147,22 @@ class Items(object):
|
||||||
userdata['Resume'],
|
userdata['Resume'],
|
||||||
userdata['Runtime'],
|
userdata['Runtime'],
|
||||||
userdata['PlayCount'],
|
userdata['PlayCount'],
|
||||||
userdata['LastPlayedDate'])
|
userdata['LastPlayedDate'],
|
||||||
|
api.plex_type())
|
||||||
if v.KODIVERSION >= 17:
|
if v.KODIVERSION >= 17:
|
||||||
self.kodi_db.update_userrating(db_item[0],
|
self.kodi_db.update_userrating(db_item[0],
|
||||||
db_item[4],
|
db_item[4],
|
||||||
userdata['UserRating'])
|
userdata['UserRating'])
|
||||||
|
|
||||||
def updatePlaystate(self, mark_played, view_count, resume, duration,
|
def updatePlaystate(self, mark_played, view_count, resume, duration,
|
||||||
file_id, lastViewedAt):
|
file_id, lastViewedAt, plex_type):
|
||||||
"""
|
"""
|
||||||
Use with websockets, not xml
|
Use with websockets, not xml
|
||||||
"""
|
"""
|
||||||
# If the playback was stopped, check whether we need to increment the
|
# If the playback was stopped, check whether we need to increment the
|
||||||
# playcount. PMS won't tell us the playcount via websockets
|
# playcount. PMS won't tell us the playcount via websockets
|
||||||
LOG.debug('Set playstate for file_id %s: viewcount: %s, resume: %s',
|
LOG.debug('Playstate file_id %s: viewcount: %s, resume: %s, type: %s',
|
||||||
file_id, view_count, resume)
|
file_id, view_count, resume, plex_type)
|
||||||
if mark_played:
|
if mark_played:
|
||||||
LOG.info('Marking as completely watched in Kodi')
|
LOG.info('Marking as completely watched in Kodi')
|
||||||
try:
|
try:
|
||||||
|
@ -174,7 +175,8 @@ class Items(object):
|
||||||
resume,
|
resume,
|
||||||
duration,
|
duration,
|
||||||
view_count,
|
view_count,
|
||||||
lastViewedAt)
|
lastViewedAt,
|
||||||
|
plex_type)
|
||||||
|
|
||||||
|
|
||||||
class Movies(Items):
|
class Movies(Items):
|
||||||
|
@ -448,7 +450,12 @@ class Movies(Items):
|
||||||
# Add any sets from Plex collection tags
|
# Add any sets from Plex collection tags
|
||||||
self.kodi_db.addSets(movieid, collections, kodicursor)
|
self.kodi_db.addSets(movieid, collections, kodicursor)
|
||||||
# Process playstates
|
# Process playstates
|
||||||
self.kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
self.kodi_db.addPlaystate(fileid,
|
||||||
|
resume,
|
||||||
|
runtime,
|
||||||
|
playcount,
|
||||||
|
dateplayed,
|
||||||
|
v.PLEX_TYPE_MOVIE)
|
||||||
|
|
||||||
def remove(self, plex_id):
|
def remove(self, plex_id):
|
||||||
"""
|
"""
|
||||||
|
@ -584,7 +591,8 @@ class TVShows(Items):
|
||||||
# Set plugin path
|
# Set plugin path
|
||||||
toplevelpath = "plugin://%s.tvshows/" % v.ADDON_ID
|
toplevelpath = "plugin://%s.tvshows/" % v.ADDON_ID
|
||||||
path = "%s%s/" % (toplevelpath, itemid)
|
path = "%s%s/" % (toplevelpath, itemid)
|
||||||
toppathid = self.kodi_db.get_path(toplevelpath)
|
# Do NOT set a parent id because addon-path cannot be "stacked"
|
||||||
|
toppathid = None
|
||||||
|
|
||||||
pathid = self.kodi_db.add_video_path(path,
|
pathid = self.kodi_db.add_video_path(path,
|
||||||
date_added=api.date_created(),
|
date_added=api.date_created(),
|
||||||
|
@ -989,7 +997,24 @@ class TVShows(Items):
|
||||||
resume,
|
resume,
|
||||||
runtime,
|
runtime,
|
||||||
playcount,
|
playcount,
|
||||||
dateplayed)
|
dateplayed,
|
||||||
|
None) # Do send None, we check here
|
||||||
|
if not state.DIRECT_PATHS:
|
||||||
|
# need to set a SECOND file entry for a path without plex show id
|
||||||
|
filename = api.file_name(force_first_media=True)
|
||||||
|
path = 'plugin://%s.tvshows/' % v.ADDON_ID
|
||||||
|
# Filename is exactly the same, WITH plex show id!
|
||||||
|
filename = ('%s%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
|
% (path, series_id, itemid, v.PLEX_TYPE_EPISODE,
|
||||||
|
filename))
|
||||||
|
pathid = self.kodi_db.add_video_path(path)
|
||||||
|
fileid = self.kodi_db.add_file(filename, pathid, dateadded)
|
||||||
|
self.kodi_db.addPlaystate(fileid,
|
||||||
|
resume,
|
||||||
|
runtime,
|
||||||
|
playcount,
|
||||||
|
dateplayed,
|
||||||
|
None) # Do send None - 2nd entry
|
||||||
|
|
||||||
@catch_exceptions(warnuser=True)
|
@catch_exceptions(warnuser=True)
|
||||||
def remove(self, plex_id):
|
def remove(self, plex_id):
|
||||||
|
@ -1093,7 +1118,7 @@ class TVShows(Items):
|
||||||
Remove an episode, and episode only from the Kodi DB (not Plex DB)
|
Remove an episode, and episode only from the Kodi DB (not Plex DB)
|
||||||
"""
|
"""
|
||||||
self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.remove_file(file_id)
|
self.kodi_db.remove_file(file_id, plex_type=v.PLEX_TYPE_EPISODE)
|
||||||
self.artwork.delete_artwork(kodi_id,
|
self.artwork.delete_artwork(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
self.kodicursor)
|
self.kodicursor)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from sqlite3 import IntegrityError
|
||||||
import artwork
|
import artwork
|
||||||
from utils import kodi_sql, try_decode, unix_timestamp, unix_date_to_kodi
|
from utils import kodi_sql, try_decode, unix_timestamp, unix_date_to_kodi
|
||||||
import variables as v
|
import variables as v
|
||||||
|
import state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -237,13 +238,31 @@ class KodiDBMethods(object):
|
||||||
show_id = None
|
show_id = None
|
||||||
return show_id
|
return show_id
|
||||||
|
|
||||||
def remove_file(self, file_id, remove_orphans=True):
|
def remove_file(self, file_id, remove_orphans=True, plex_type=None):
|
||||||
"""
|
"""
|
||||||
Removes the entry for file_id from the files table. Will also delete
|
Removes the entry for file_id from the files table. Will also delete
|
||||||
entries from the associated tables: bookmark, settings, streamdetails.
|
entries from the associated tables: bookmark, settings, streamdetails.
|
||||||
If remove_orphans is true, this method will delete any orphaned path
|
If remove_orphans is true, this method will delete any orphaned path
|
||||||
entries in the Kodi path table
|
entries in the Kodi path table
|
||||||
|
|
||||||
|
Passing plex_type = v.PLEX_TYPE_EPISODE deletes any secondary files for
|
||||||
|
add-on paths
|
||||||
"""
|
"""
|
||||||
|
if not state.DIRECT_PATHS and plex_type == v.PLEX_TYPE_EPISODE:
|
||||||
|
# Hack for the 2 entries for episodes for addon paths
|
||||||
|
query = 'SELECT strFilename FROM files WHERE idFile = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (file_id, ))
|
||||||
|
filename = self.cursor.fetchone()
|
||||||
|
if not filename:
|
||||||
|
LOG.error('Could not find file_id %s', file_id)
|
||||||
|
return
|
||||||
|
query = 'SELECT idFile FROM files WHERE strFilename = ? LIMIT 2'
|
||||||
|
self.cursor.execute(query, (filename[0], ))
|
||||||
|
file_ids = self.cursor.fetchall()
|
||||||
|
for new_id in file_ids:
|
||||||
|
self.remove_file(new_id[0], remove_orphans=remove_orphans)
|
||||||
|
return
|
||||||
|
|
||||||
self.cursor.execute('SELECT idPath FROM files WHERE idFile = ? LIMIT 1',
|
self.cursor.execute('SELECT idPath FROM files WHERE idFile = ? LIMIT 1',
|
||||||
(file_id,))
|
(file_id,))
|
||||||
try:
|
try:
|
||||||
|
@ -708,7 +727,29 @@ class KodiDBMethods(object):
|
||||||
return answ
|
return answ
|
||||||
|
|
||||||
def addPlaystate(self, file_id, resume_seconds, total_seconds, playcount,
|
def addPlaystate(self, file_id, resume_seconds, total_seconds, playcount,
|
||||||
dateplayed):
|
dateplayed, plex_type):
|
||||||
|
if not state.DIRECT_PATHS and plex_type == v.PLEX_TYPE_EPISODE:
|
||||||
|
# Need to make sure to set a SECOND bookmark entry for another,
|
||||||
|
# second file_id that points to the path .tvshows instead of
|
||||||
|
# .tvshows/<plex show id/!
|
||||||
|
query = 'SELECT strFilename FROM files WHERE idFile = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (file_id, ))
|
||||||
|
filename = self.cursor.fetchone()
|
||||||
|
if not filename:
|
||||||
|
LOG.error('Could not find fileid %s in Kodi DB', file_id)
|
||||||
|
raise RuntimeError
|
||||||
|
query = 'SELECT idFile FROM files WHERE strFilename = ? LIMIT 2'
|
||||||
|
self.cursor.execute(query, (filename[0], ))
|
||||||
|
file_ids = self.cursor.fetchall()
|
||||||
|
if len(file_ids) != 2:
|
||||||
|
LOG.error('Expected to find 2 file ids but found %s',
|
||||||
|
len(file_ids))
|
||||||
|
raise RuntimeError
|
||||||
|
for new_id in file_ids:
|
||||||
|
self.addPlaystate(new_id[0], resume_seconds, total_seconds,
|
||||||
|
playcount, dateplayed, None)
|
||||||
|
return
|
||||||
|
|
||||||
# Delete existing resume point
|
# Delete existing resume point
|
||||||
self.cursor.execute('DELETE FROM bookmark WHERE idFile = ?', (file_id,))
|
self.cursor.execute('DELETE FROM bookmark WHERE idFile = ?', (file_id,))
|
||||||
# Set watched count
|
# Set watched count
|
||||||
|
|
|
@ -523,7 +523,8 @@ def _record_playstate(status, ended):
|
||||||
time,
|
time,
|
||||||
totaltime,
|
totaltime,
|
||||||
playcount,
|
playcount,
|
||||||
last_played)
|
last_played,
|
||||||
|
status['plex_type'])
|
||||||
# Hack to force "in progress" widget to appear if it wasn't visible before
|
# Hack to force "in progress" widget to appear if it wasn't visible before
|
||||||
if (state.FORCE_RELOAD_SKIN and
|
if (state.FORCE_RELOAD_SKIN and
|
||||||
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
|
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
|
||||||
|
|
|
@ -1357,12 +1357,14 @@ class LibrarySync(Thread):
|
||||||
item_fkt = getattr(itemtypes,
|
item_fkt = getattr(itemtypes,
|
||||||
v.ITEMTYPE_FROM_KODITYPE[session['kodi_type']])
|
v.ITEMTYPE_FROM_KODITYPE[session['kodi_type']])
|
||||||
with item_fkt() as fkt:
|
with item_fkt() as fkt:
|
||||||
|
plex_type = v.PLEX_TYPE_FROM_KODI_TYPE[session['kodi_type']]
|
||||||
fkt.updatePlaystate(mark_played,
|
fkt.updatePlaystate(mark_played,
|
||||||
session['viewCount'],
|
session['viewCount'],
|
||||||
resume,
|
resume,
|
||||||
session['duration'],
|
session['duration'],
|
||||||
session['file_id'],
|
session['file_id'],
|
||||||
utils.unix_date_to_kodi(utils.unix_timestamp()))
|
utils.unix_date_to_kodi(utils.unix_timestamp()),
|
||||||
|
plex_type)
|
||||||
|
|
||||||
def sync_fanart(self, missing_only=True, refresh=False):
|
def sync_fanart(self, missing_only=True, refresh=False):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -241,11 +241,7 @@ def _prep_playlist_stack(xml):
|
||||||
api.set_part_number(part)
|
api.set_part_number(part)
|
||||||
if kodi_id is None:
|
if kodi_id is None:
|
||||||
# Need to redirect again to PKC to conclude playback
|
# Need to redirect again to PKC to conclude playback
|
||||||
path = ('plugin://%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
path = api.path()
|
||||||
% (v.ADDON_TYPE[api.plex_type()],
|
|
||||||
api.plex_id(),
|
|
||||||
api.plex_type(),
|
|
||||||
api.file_name(force_first_media=True)))
|
|
||||||
listitem = api.create_listitem()
|
listitem = api.create_listitem()
|
||||||
listitem.setPath(try_encode(path))
|
listitem.setPath(try_encode(path))
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in a new issue