Merge pull request #710 from croneter/select-library
Choose which Plex libraries get synched to Kodi
This commit is contained in:
commit
aa27b4ad1f
10 changed files with 262 additions and 130 deletions
|
@ -141,6 +141,10 @@ class Main():
|
|||
elif mode == 'hub':
|
||||
entrypoint.hub(params.get('type'))
|
||||
|
||||
elif mode == 'select-libraries':
|
||||
LOG.info('User requested to select Plex libraries')
|
||||
transfer.plex_command('select-libraries')
|
||||
|
||||
else:
|
||||
entrypoint.show_main_menu(content_type=params.get('content_type'))
|
||||
|
||||
|
|
|
@ -558,6 +558,12 @@ msgctxt "#30523"
|
|||
msgid "Also show sync progress for playstate and user data"
|
||||
msgstr ""
|
||||
|
||||
# PKC Settings - Sync Options
|
||||
msgctxt "#30524"
|
||||
msgid "Select Plex libraries to sync"
|
||||
msgstr ""
|
||||
|
||||
|
||||
# PKC Settings - Playback
|
||||
msgctxt "#30527"
|
||||
msgid "Ignore specials in next episodes"
|
||||
|
|
|
@ -7,3 +7,5 @@ from .websocket import store_websocket_message, process_websocket_messages, \
|
|||
WEBSOCKET_MESSAGES, PLAYSTATE_SESSIONS
|
||||
from .common import update_kodi_library, PLAYLIST_SYNC_ENABLED
|
||||
from .fanart import FanartThread, FanartTask
|
||||
from .videonodes import VideoNodes
|
||||
from .sections import force_full_sync
|
||||
|
|
|
@ -68,6 +68,7 @@ class FullSync(common.fullsync_mixin):
|
|||
self.context = None
|
||||
self.get_children = None
|
||||
self.successful = None
|
||||
self.section_success = None
|
||||
self.install_sync_done = utils.settings('SyncInstallRunDone') == 'true'
|
||||
self.threader = backgroundthread.ThreaderManager(
|
||||
worker=backgroundthread.NonstoppingBackgroundWorker,
|
||||
|
@ -232,8 +233,8 @@ class FullSync(common.fullsync_mixin):
|
|||
if not itemtype.update_userdata(xml_item, section['plex_type']):
|
||||
# Somehow did not sync this item yet
|
||||
itemtype.add_update(xml_item,
|
||||
section['section_name'],
|
||||
section['section_id'])
|
||||
section_name=section['section_name'],
|
||||
section_id=section['section_id'])
|
||||
itemtype.plexdb.update_last_sync(int(xml_item.attrib['ratingKey']),
|
||||
section['plex_type'],
|
||||
self.current_sync)
|
||||
|
@ -248,38 +249,39 @@ class FullSync(common.fullsync_mixin):
|
|||
LOG.error('Could not entirely process section %s', section)
|
||||
return False
|
||||
|
||||
def threaded_get_iterators(self, kinds, queue, updated_at=None,
|
||||
last_viewed_at=None):
|
||||
def threaded_get_iterators(self, kinds, queue, all_items=False):
|
||||
"""
|
||||
PF.SectionItems is costly, so let's do it asynchronous
|
||||
"""
|
||||
if self.repair:
|
||||
updated_at = None
|
||||
last_viewed_at = None
|
||||
else:
|
||||
updated_at = updated_at - UPDATED_AT_SAFETY if updated_at else None
|
||||
last_viewed_at = last_viewed_at - LAST_VIEWED_AT_SAFETY \
|
||||
if last_viewed_at else None
|
||||
try:
|
||||
for kind in kinds:
|
||||
for section in (x for x in sections.SECTIONS
|
||||
if x['plex_type'] == kind[1]):
|
||||
if self.isCanceled():
|
||||
return
|
||||
if not section['sync_to_kodi']:
|
||||
LOG.info('User chose to not sync section %s', section)
|
||||
continue
|
||||
element = copy.deepcopy(section)
|
||||
element['section_type'] = element['plex_type']
|
||||
element['plex_type'] = kind[0]
|
||||
element['element_type'] = kind[1]
|
||||
element['context'] = kind[2]
|
||||
element['get_children'] = kind[3]
|
||||
if self.repair or all_items:
|
||||
updated_at = None
|
||||
else:
|
||||
updated_at = section['last_sync'] - UPDATED_AT_SAFETY \
|
||||
if section['last_sync'] else None
|
||||
try:
|
||||
element['iterator'] = PF.SectionItems(section['section_id'],
|
||||
plex_type=kind[0],
|
||||
updated_at=updated_at,
|
||||
last_viewed_at=last_viewed_at)
|
||||
last_viewed_at=None)
|
||||
except RuntimeError:
|
||||
LOG.warn('Sync at least partially unsuccessful')
|
||||
self.successful = False
|
||||
self.section_success = False
|
||||
else:
|
||||
queue.put(element)
|
||||
finally:
|
||||
|
@ -303,14 +305,13 @@ class FullSync(common.fullsync_mixin):
|
|||
# Already start setting up the iterators. We need to enforce
|
||||
# syncing e.g. show before season before episode
|
||||
iterator_queue = Queue.Queue()
|
||||
updated_at = int(utils.settings('lastfullsync')) or None
|
||||
task = backgroundthread.FunctionAsTask(self.threaded_get_iterators,
|
||||
None,
|
||||
kinds,
|
||||
iterator_queue,
|
||||
updated_at=updated_at)
|
||||
iterator_queue)
|
||||
backgroundthread.BGThreader.addTask(task)
|
||||
while True:
|
||||
self.section_success = True
|
||||
section = iterator_queue.get()
|
||||
iterator_queue.task_done()
|
||||
if section is None:
|
||||
|
@ -323,10 +324,14 @@ class FullSync(common.fullsync_mixin):
|
|||
# Now do the heavy lifting
|
||||
if self.isCanceled() or not self.addupdate_section(section):
|
||||
return False
|
||||
if self.section_success:
|
||||
# Need to check because a thread might have missed to get
|
||||
# some items from the PMS
|
||||
with PlexDB() as plexdb:
|
||||
# Set the new time mark for the next delta sync
|
||||
plexdb.update_section_last_sync(section['section_id'],
|
||||
self.current_sync)
|
||||
common.update_kodi_library(video=True, music=True)
|
||||
if self.successful:
|
||||
# Set timestamp for next sync - neglecting playstates!
|
||||
utils.settings('lastfullsync', value=str(int(self.current_sync)))
|
||||
# In order to not delete all your songs again
|
||||
if app.SYNC.enable_music:
|
||||
kinds.extend([
|
||||
|
@ -347,7 +352,8 @@ class FullSync(common.fullsync_mixin):
|
|||
task = backgroundthread.FunctionAsTask(self.threaded_get_iterators,
|
||||
None,
|
||||
kinds,
|
||||
iterator_queue)
|
||||
iterator_queue,
|
||||
all_items=True)
|
||||
backgroundthread.BGThreader.addTask(task)
|
||||
while True:
|
||||
section = iterator_queue.get()
|
||||
|
@ -364,7 +370,7 @@ class FullSync(common.fullsync_mixin):
|
|||
return False
|
||||
|
||||
# Delete movies that are not on Plex anymore
|
||||
LOG.info('Looking for items to delete')
|
||||
LOG.debug('Looking for items to delete')
|
||||
kinds = [
|
||||
(v.PLEX_TYPE_MOVIE, itemtypes.Movie),
|
||||
(v.PLEX_TYPE_SHOW, itemtypes.Show),
|
||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import absolute_import, division, unicode_literals
|
|||
from logging import getLogger
|
||||
import copy
|
||||
|
||||
from . import common, videonodes
|
||||
from . import videonodes
|
||||
from ..utils import cast
|
||||
from ..plex_db import PlexDB
|
||||
from .. import kodi_db
|
||||
|
@ -22,6 +22,16 @@ SECTIONS = []
|
|||
IS_CANCELED = None
|
||||
|
||||
|
||||
def force_full_sync():
|
||||
"""
|
||||
Resets the sync timestamp for all sections to 0, thus forcing a subsequent
|
||||
full sync (not delta)
|
||||
"""
|
||||
LOG.info('Telling PKC to do a full sync instead of a delta sync')
|
||||
with PlexDB() as plexdb:
|
||||
plexdb.force_full_sync()
|
||||
|
||||
|
||||
def sync_from_pms(parent_self):
|
||||
"""
|
||||
Sync the Plex library sections
|
||||
|
@ -35,6 +45,7 @@ def sync_from_pms(parent_self):
|
|||
|
||||
|
||||
def _sync_from_pms():
|
||||
global PLAYLISTS, NODES, SECTIONS
|
||||
sections = PF.get_plex_sections()
|
||||
try:
|
||||
sections.attrib
|
||||
|
@ -45,7 +56,7 @@ def _sync_from_pms():
|
|||
# Will reboot Kodi is new library detected
|
||||
music.excludefromscan_music_folders(xml=sections)
|
||||
|
||||
global PLAYLISTS, NODES, SECTIONS
|
||||
VNODES.clearProperties()
|
||||
SECTIONS = []
|
||||
NODES = {
|
||||
v.PLEX_TYPE_MOVIE: [],
|
||||
|
@ -54,64 +65,51 @@ def _sync_from_pms():
|
|||
v.PLEX_TYPE_PHOTO: []
|
||||
}
|
||||
PLAYLISTS = copy.deepcopy(NODES)
|
||||
sorted_sections = []
|
||||
|
||||
for section in sections:
|
||||
if (section.attrib['type'] in
|
||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW, v.PLEX_TYPE_PHOTO,
|
||||
v.PLEX_TYPE_ARTIST)):
|
||||
sorted_sections.append(section.attrib['title'])
|
||||
LOG.debug('Sorted sections: %s', sorted_sections)
|
||||
totalnodes = len(sorted_sections)
|
||||
|
||||
VNODES.clearProperties()
|
||||
|
||||
with PlexDB() as plexdb:
|
||||
# Backup old sections to delete them later, if needed (at the end
|
||||
# of this method, only unused sections will be left in old_sections)
|
||||
old_sections = list(plexdb.section_ids())
|
||||
old_sections = list(plexdb.all_sections())
|
||||
with kodi_db.KodiVideoDB() as kodidb:
|
||||
for section in sections:
|
||||
for index, section in enumerate(sections):
|
||||
_process_section(section,
|
||||
kodidb,
|
||||
plexdb,
|
||||
sorted_sections,
|
||||
old_sections,
|
||||
totalnodes)
|
||||
index,
|
||||
old_sections)
|
||||
if old_sections:
|
||||
# Section has been deleted on the PMS
|
||||
delete_sections(old_sections)
|
||||
# update sections for all:
|
||||
with PlexDB() as plexdb:
|
||||
SECTIONS = list(plexdb.section_infos())
|
||||
utils.window('Plex.nodes.total', str(totalnodes))
|
||||
LOG.info("Finished processing library sections: %s", SECTIONS)
|
||||
SECTIONS = list(plexdb.all_sections())
|
||||
utils.window('Plex.nodes.total', str(len(sections)))
|
||||
LOG.info("Finished processing %s library sections: %s", len(sections), SECTIONS)
|
||||
if app.CONN.machine_identifier != utils.settings('sections_asked_for_machine_identifier'):
|
||||
LOG.info('First time connecting to this PMS, choosing libraries')
|
||||
if choose_libraries():
|
||||
with PlexDB() as plexdb:
|
||||
SECTIONS = list(plexdb.all_sections())
|
||||
return True
|
||||
|
||||
|
||||
def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
||||
old_sections, totalnodes):
|
||||
def _process_section(section_xml, kodidb, plexdb, index, old_sections):
|
||||
global PLAYLISTS, NODES
|
||||
folder = section_xml.attrib
|
||||
plex_type = folder['type']
|
||||
# Only process supported formats
|
||||
if plex_type not in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW,
|
||||
v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_PHOTO):
|
||||
LOG.error('Unsupported Plex section type: %s', folder)
|
||||
return totalnodes
|
||||
return
|
||||
section_id = cast(int, folder['key'])
|
||||
section_name = folder['title']
|
||||
global PLAYLISTS, NODES
|
||||
# Prevent duplicate for nodes of the same type
|
||||
nodes = NODES[plex_type]
|
||||
# Prevent duplicate for playlists of the same type
|
||||
playlists = PLAYLISTS[plex_type]
|
||||
# Get current media folders from plex database
|
||||
section = plexdb.section(section_id)
|
||||
try:
|
||||
current_sectionname = section[1]
|
||||
current_sectiontype = section[2]
|
||||
current_tagid = section[3]
|
||||
except TypeError:
|
||||
if not section:
|
||||
LOG.info('Creating section id: %s in Plex database.', section_id)
|
||||
tagid = kodidb.create_tag(section_name)
|
||||
# Create playlist for the video library
|
||||
|
@ -121,28 +119,30 @@ def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
|||
playlists.append(section_name)
|
||||
# Create the video node
|
||||
if section_name not in nodes:
|
||||
VNODES.viewNode(sorted_sections.index(section_name),
|
||||
VNODES.viewNode(index,
|
||||
section_name,
|
||||
plex_type,
|
||||
None,
|
||||
section_id)
|
||||
nodes.append(section_name)
|
||||
totalnodes += 1
|
||||
# Add view to plex database
|
||||
plexdb.add_section(section_id, section_name, plex_type, tagid)
|
||||
plexdb.add_section(section_id,
|
||||
section_name,
|
||||
plex_type,
|
||||
tagid,
|
||||
True, # Sync this new section for now
|
||||
None)
|
||||
else:
|
||||
LOG.info('Found library section id %s, name %s, type %s, tagid %s',
|
||||
section_id, current_sectionname, current_sectiontype,
|
||||
current_tagid)
|
||||
section_id, section['section_name'], section['plex_type'],
|
||||
section['kodi_tagid'])
|
||||
# Remove views that are still valid to delete rest later
|
||||
try:
|
||||
old_sections.remove(section_id)
|
||||
except ValueError:
|
||||
# View was just created, nothing to remove
|
||||
pass
|
||||
|
||||
for section in old_sections:
|
||||
if section['section_id'] == section_id:
|
||||
old_sections.remove(section)
|
||||
break
|
||||
# View was modified, update with latest info
|
||||
if current_sectionname != section_name:
|
||||
if section['section_name'] != section_name:
|
||||
LOG.info('section id: %s new sectionname: %s',
|
||||
section_id, section_name)
|
||||
tagid = kodidb.create_tag(section_name)
|
||||
|
@ -151,22 +151,24 @@ def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
|||
plexdb.add_section(section_id,
|
||||
section_name,
|
||||
plex_type,
|
||||
tagid)
|
||||
tagid,
|
||||
section['sync_to_kodi'], # Use "old" setting
|
||||
section['last_sync'])
|
||||
|
||||
if plexdb.section_id_by_name(current_sectionname) is None:
|
||||
if plexdb.section_id_by_name(section['section_name']) is None:
|
||||
# The tag could be a combined view. Ensure there's
|
||||
# no other tags with the same name before deleting
|
||||
# playlist.
|
||||
utils.playlist_xsp(plex_type,
|
||||
current_sectionname,
|
||||
section['section_name'],
|
||||
section_id,
|
||||
current_sectiontype,
|
||||
section['plex_type'],
|
||||
True)
|
||||
# Delete video node
|
||||
if plex_type != "musicvideos":
|
||||
VNODES.viewNode(
|
||||
indexnumber=sorted_sections.index(section_name),
|
||||
tagname=current_sectionname,
|
||||
indexnumber=index,
|
||||
tagname=section['section_name'],
|
||||
mediatype=plex_type,
|
||||
viewtype=None,
|
||||
viewid=section_id,
|
||||
|
@ -179,17 +181,16 @@ def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
|||
playlists.append(section_name)
|
||||
# Add new video node
|
||||
if section_name not in nodes and plex_type != "musicvideos":
|
||||
VNODES.viewNode(sorted_sections.index(section_name),
|
||||
VNODES.viewNode(index,
|
||||
section_name,
|
||||
plex_type,
|
||||
None,
|
||||
section_id)
|
||||
nodes.append(section_name)
|
||||
totalnodes += 1
|
||||
# Update items with new tag
|
||||
for kodi_id in plexdb.kodiid_by_sectionid(section_id, plex_type):
|
||||
kodidb.update_tag(
|
||||
current_tagid, tagid, kodi_id, current_sectiontype)
|
||||
section['kodi_tagid'], tagid, kodi_id, section['plex_type'])
|
||||
else:
|
||||
# Validate the playlist exists or recreate it
|
||||
if (section_name not in playlists and plex_type in
|
||||
|
@ -200,14 +201,12 @@ def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
|||
playlists.append(section_name)
|
||||
# Create the video node if not already exists
|
||||
if section_name not in nodes and plex_type != "musicvideos":
|
||||
VNODES.viewNode(sorted_sections.index(section_name),
|
||||
VNODES.viewNode(index,
|
||||
section_name,
|
||||
plex_type,
|
||||
None,
|
||||
section_id)
|
||||
nodes.append(section_name)
|
||||
totalnodes += 1
|
||||
return totalnodes
|
||||
|
||||
|
||||
def _delete_kodi_db_items(section_id, section_type):
|
||||
|
@ -246,25 +245,69 @@ def delete_sections(old_sections):
|
|||
Deletes all elements for a Plex section that has been deleted. (e.g. all
|
||||
TV shows, Seasons and Episodes of a Show section)
|
||||
"""
|
||||
try:
|
||||
LOG.info("Removing entire Plex library sections: %s", old_sections)
|
||||
for section in old_sections:
|
||||
# "Deleting <section_name>"
|
||||
utils.dialog('notification',
|
||||
heading='{plex}',
|
||||
message='%s %s' % (utils.lang(30052), section['section_name']),
|
||||
icon='{plex}',
|
||||
sound=False)
|
||||
if section['plex_type'] == v.PLEX_TYPE_PHOTO:
|
||||
# not synced - just remove the link in our Plex sections table
|
||||
pass
|
||||
else:
|
||||
if not _delete_kodi_db_items(section['section_id'], section['plex_type']):
|
||||
return
|
||||
# Only remove Plex entry if we've removed all items first
|
||||
with PlexDB() as plexdb:
|
||||
old_sections = [plexdb.section(x) for x in old_sections]
|
||||
LOG.info("Removing entire Plex library sections: %s", old_sections)
|
||||
for section in old_sections:
|
||||
# "Deleting <section_name>"
|
||||
utils.dialog('notification',
|
||||
heading='{plex}',
|
||||
message='%s %s' % (utils.lang(30052), section[1]),
|
||||
icon='{plex}',
|
||||
sound=False)
|
||||
if section[2] == v.PLEX_TYPE_PHOTO:
|
||||
# not synced - just remove the link in our Plex sections table
|
||||
pass
|
||||
plexdb.remove_section(section['section_id'])
|
||||
|
||||
|
||||
def choose_libraries():
|
||||
"""
|
||||
Displays a dialog for the user to select the libraries he wants synched
|
||||
|
||||
Returns True if this was successful, False if not
|
||||
"""
|
||||
# Re-set value in order to make sure we got the lastest user input
|
||||
app.SYNC.enable_music = utils.settings('enableMusic') == 'true'
|
||||
import xbmcgui
|
||||
sections = []
|
||||
preselect = []
|
||||
index = 0
|
||||
for section in SECTIONS:
|
||||
if not app.SYNC.enable_music and section['plex_type'] == v.PLEX_TYPE_ARTIST:
|
||||
LOG.info('Ignoring music section: %s', section)
|
||||
continue
|
||||
elif section['plex_type'] == v.PLEX_TYPE_PHOTO:
|
||||
continue
|
||||
else:
|
||||
sections.append(section['section_name'])
|
||||
if section['sync_to_kodi']:
|
||||
preselect.append(index)
|
||||
index += 1
|
||||
# "Select Plex libraries to sync"
|
||||
selected = xbmcgui.Dialog().multiselect(utils.lang(30524),
|
||||
sections,
|
||||
preselect=preselect,
|
||||
useDetails=False)
|
||||
if selected is None:
|
||||
# User canceled
|
||||
return False
|
||||
index = 0
|
||||
with PlexDB() as plexdb:
|
||||
for section in SECTIONS:
|
||||
if not app.SYNC.enable_music and section['plex_type'] == v.PLEX_TYPE_ARTIST:
|
||||
continue
|
||||
elif section['plex_type'] == v.PLEX_TYPE_PHOTO:
|
||||
continue
|
||||
else:
|
||||
if not _delete_kodi_db_items(section[0], section[2]):
|
||||
return
|
||||
# Only remove Plex entry if we've removed all items first
|
||||
with PlexDB() as plexdb:
|
||||
plexdb.remove_section(section[0])
|
||||
finally:
|
||||
common.update_kodi_library()
|
||||
sync = True if index in selected else False
|
||||
plexdb.update_section_sync(section['section_id'], sync)
|
||||
index += 1
|
||||
sections = list(plexdb.all_sections())
|
||||
LOG.info('Plex libraries to sync: %s', sections)
|
||||
utils.settings('sections_asked_for_machine_identifier',
|
||||
value=app.CONN.machine_identifier)
|
||||
return True
|
||||
|
|
|
@ -194,7 +194,8 @@ def initialize():
|
|||
section_name TEXT,
|
||||
plex_type TEXT,
|
||||
kodi_tagid INTEGER,
|
||||
sync_to_kodi INTEGER)
|
||||
sync_to_kodi INTEGER,
|
||||
last_sync INTEGER)
|
||||
''')
|
||||
plexdb.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS movie(
|
||||
|
|
|
@ -4,43 +4,39 @@ from __future__ import absolute_import, division, unicode_literals
|
|||
|
||||
|
||||
class Sections(object):
|
||||
def section_ids(self):
|
||||
def all_sections(self):
|
||||
"""
|
||||
Returns an iterator for section Plex ids for all sections
|
||||
"""
|
||||
self.cursor.execute('SELECT section_id FROM sections')
|
||||
return (x[0] for x in self.cursor)
|
||||
|
||||
def section_infos(self):
|
||||
"""
|
||||
Returns an iterator for dicts for all Plex libraries:
|
||||
{
|
||||
'section_id'
|
||||
'section_name'
|
||||
'plex_type'
|
||||
'kodi_tagid'
|
||||
'sync_to_kodi'
|
||||
}
|
||||
Returns an iterator for all sections
|
||||
"""
|
||||
self.cursor.execute('SELECT * FROM sections')
|
||||
return ({'section_id': x[0],
|
||||
'section_name': x[1],
|
||||
'plex_type': x[2],
|
||||
'kodi_tagid': x[3],
|
||||
'sync_to_kodi': x[4]} for x in self.cursor)
|
||||
return (self.entry_to_section(x) for x in self.cursor)
|
||||
|
||||
def section(self, section_id):
|
||||
"""
|
||||
For section_id, returns the tuple (or None)
|
||||
For section_id, returns the dict
|
||||
section_id INTEGER PRIMARY KEY,
|
||||
section_name TEXT,
|
||||
plex_type TEXT,
|
||||
kodi_tagid INTEGER,
|
||||
sync_to_kodi INTEGER
|
||||
sync_to_kodi BOOL,
|
||||
last_sync INTEGER
|
||||
"""
|
||||
self.cursor.execute('SELECT * FROM sections WHERE section_id = ? LIMIT 1',
|
||||
(section_id, ))
|
||||
return self.cursor.fetchone()
|
||||
return self.entry_to_section(self.cursor.fetchone())
|
||||
|
||||
@staticmethod
|
||||
def entry_to_section(entry):
|
||||
if not entry:
|
||||
return
|
||||
return {
|
||||
'section_id': entry[0],
|
||||
'section_name': entry[1],
|
||||
'plex_type': entry[2],
|
||||
'kodi_tagid': entry[3],
|
||||
'sync_to_kodi': entry[4] == 1,
|
||||
'last_sync': entry[5]
|
||||
}
|
||||
|
||||
def section_id_by_name(self, section_name):
|
||||
"""
|
||||
|
@ -54,22 +50,35 @@ class Sections(object):
|
|||
pass
|
||||
|
||||
def add_section(self, section_id, section_name, plex_type, kodi_tagid,
|
||||
sync_to_kodi=True):
|
||||
sync_to_kodi, last_sync):
|
||||
"""
|
||||
Appends a Plex section to the Plex sections table
|
||||
sync=False: Plex library won't be synced to Kodi
|
||||
"""
|
||||
query = '''
|
||||
INSERT OR REPLACE INTO sections(
|
||||
section_id, section_name, plex_type, kodi_tagid, sync_to_kodi)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
section_id,
|
||||
section_name,
|
||||
plex_type,
|
||||
kodi_tagid,
|
||||
sync_to_kodi,
|
||||
last_sync)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
self.cursor.execute(query,
|
||||
(section_id,
|
||||
section_name,
|
||||
plex_type,
|
||||
kodi_tagid,
|
||||
sync_to_kodi))
|
||||
sync_to_kodi,
|
||||
last_sync))
|
||||
|
||||
def update_section(self, section_id, section_name):
|
||||
"""
|
||||
Updates the section with section_id
|
||||
"""
|
||||
query = 'UPDATE sections SET section_name = ? WHERE section_id = ?'
|
||||
self.cursor.execute(query, (section_name, section_id))
|
||||
|
||||
def remove_section(self, section_id):
|
||||
"""
|
||||
|
@ -77,3 +86,35 @@ class Sections(object):
|
|||
"""
|
||||
self.cursor.execute('DELETE FROM sections WHERE section_id = ?',
|
||||
(section_id, ))
|
||||
|
||||
def update_section_sync(self, section_id, sync_to_kodi):
|
||||
"""
|
||||
Updates whether we should sync sections_id (sync=True) or not
|
||||
"""
|
||||
if sync_to_kodi:
|
||||
query = '''
|
||||
UPDATE sections
|
||||
SET sync_to_kodi = ?
|
||||
WHERE section_id = ?
|
||||
'''
|
||||
else:
|
||||
# Set last_sync = 0 in order to force a full sync if reactivated
|
||||
query = '''
|
||||
UPDATE sections
|
||||
SET sync_to_kodi = ?, last_sync = 0
|
||||
WHERE section_id = ?
|
||||
'''
|
||||
self.cursor.execute(query, (sync_to_kodi, section_id))
|
||||
|
||||
def update_section_last_sync(self, section_id, last_sync):
|
||||
"""
|
||||
Updates the timestamp for the section
|
||||
"""
|
||||
self.cursor.execute('UPDATE sections SET last_sync = ? WHERE section_id = ?',
|
||||
(last_sync, section_id))
|
||||
|
||||
def force_full_sync(self):
|
||||
"""
|
||||
Sets the last_sync flag to 0 for every section
|
||||
"""
|
||||
self.cursor.execute('UPDATE sections SET last_sync = 0')
|
||||
|
|
|
@ -9,7 +9,7 @@ import xbmcgui
|
|||
from . import utils, clientinfo, timing
|
||||
from . import initialsetup
|
||||
from . import kodimonitor
|
||||
from . import sync
|
||||
from . import sync, library_sync
|
||||
from . import websocket_client
|
||||
from . import plex_companion
|
||||
from . import plex_functions as PF, playqueue as PQ
|
||||
|
@ -97,8 +97,7 @@ class Service():
|
|||
|
||||
# Load/Reset PKC entirely - important for user/Kodi profile switch
|
||||
# Clear video nodes properties
|
||||
from .library_sync import videonodes
|
||||
videonodes.VideoNodes().clearProperties()
|
||||
library_sync.VideoNodes().clearProperties()
|
||||
clientinfo.getDeviceId()
|
||||
# Init time-offset between Kodi and Plex
|
||||
timing.KODI_PLEX_TIME_OFFSET = float(utils.settings('kodiplextimeoffset') or 0.0)
|
||||
|
@ -222,7 +221,7 @@ class Service():
|
|||
utils.delete_nodes()
|
||||
app.ACCOUNT.set_unauthenticated()
|
||||
# Force full sync after login
|
||||
utils.settings('lastfullsync', value='0')
|
||||
library_sync.force_full_sync()
|
||||
app.SYNC.run_lib_scan = 'full'
|
||||
# Enable the main loop to display user selection dialog
|
||||
app.APP.suspend = False
|
||||
|
@ -286,6 +285,33 @@ class Service():
|
|||
LOG.info("Entering PMS address complete")
|
||||
return True
|
||||
|
||||
def choose_plex_libraries(self):
|
||||
if not app.CONN.online:
|
||||
LOG.error('PMS not online to choose libraries')
|
||||
# "{0} offline"
|
||||
utils.dialog('notification',
|
||||
utils.lang(29999),
|
||||
utils.lang(39213).format(app.CONN.server_name or ''),
|
||||
icon='{plex}')
|
||||
return
|
||||
if not app.ACCOUNT.authenticated:
|
||||
LOG.error('Not yet authenticated for PMS to choose libraries')
|
||||
# "Unauthorized for PMS"
|
||||
utils.dialog('notification', utils.lang(29999), utils.lang(30017))
|
||||
return
|
||||
app.APP.suspend_threads()
|
||||
from .library_sync import sections
|
||||
try:
|
||||
# Get newest sections from the PMS
|
||||
if not sections.sync_from_pms(self):
|
||||
return
|
||||
if not sections.choose_libraries():
|
||||
return
|
||||
# Force a full sync
|
||||
app.SYNC.run_lib_scan = 'full'
|
||||
finally:
|
||||
app.APP.resume_threads()
|
||||
|
||||
def _do_auth(self):
|
||||
LOG.info('Authenticating user')
|
||||
if app.ACCOUNT.plex_username and not app.ACCOUNT.force_login: # Found a user in the settings, try to authenticate
|
||||
|
@ -435,6 +461,8 @@ class Service():
|
|||
app.SYNC.run_lib_scan = 'fanart'
|
||||
elif plex_command == 'textures-scan':
|
||||
app.SYNC.run_lib_scan = 'textures'
|
||||
elif plex_command == 'select-libraries':
|
||||
self.choose_plex_libraries()
|
||||
elif plex_command == 'RESET-PKC':
|
||||
utils.reset()
|
||||
if task:
|
||||
|
|
|
@ -466,7 +466,7 @@ def wipe_database():
|
|||
kodi_db.reset_cached_images()
|
||||
# reset the install run flag
|
||||
settings('SyncInstallRunDone', value="false")
|
||||
settings('lastfullsync', value="0")
|
||||
settings('sections_asked_for_machine_identifier', value='')
|
||||
init_dbs()
|
||||
LOG.info('Wiping done')
|
||||
if settings('kodi_db_has_been_wiped_clean') != 'true':
|
||||
|
|
|
@ -49,9 +49,11 @@
|
|||
<setting id="dbCreatedWithVersion" type="text" default="" visible="false"/>
|
||||
<setting id="plexid" type="text" default="" visible="false"/>
|
||||
<setting id="userid" type="text" default="" visible="false"/>
|
||||
<setting id="sections_asked_for_machine_identifier" type="text" default="" visible="false"/>
|
||||
</category>
|
||||
|
||||
<category label="30506"><!-- Sync Options -->
|
||||
<setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 30524][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=select-libraries)" option="close" help="Choose which Plex library you want to sync"/><!-- Select Plex libraries to sync -->
|
||||
<setting type="lsep" label="30537" /><!-- Restart if you make changes -->
|
||||
<setting type="sep" />
|
||||
<setting id="fullSyncInterval" type="number" label="39053" default="60" option="int" />
|
||||
|
@ -84,7 +86,6 @@
|
|||
<setting id="themoviedbAPIKey" type="text" default="19c90103adb9e98f2172c6a6a3d85dc4" visible="false"/>
|
||||
<setting id="FanArtTVAPIKey" type="text" default="639191cb0774661597f28a47e7e2bad5" visible="false"/>
|
||||
<setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/>
|
||||
<setting id="lastfullsync" type="number" label="Time stamp when last successful full sync was conducted" default="0" visible="false" />
|
||||
<setting id="kodi_db_has_been_wiped_clean" type="bool" label="PKC needs to completely clean the Kodi DB at least once, then reboot, to avoid Kodi error messages, e.g. OperationalError" default="false" visible="false" />
|
||||
</category>
|
||||
|
||||
|
|
Loading…
Reference in a new issue