Rewire llibrary sync, part 5
This commit is contained in:
parent
23dada9fe5
commit
2f96749fc7
10 changed files with 258 additions and 1151 deletions
|
@ -123,10 +123,6 @@ class Main():
|
|||
elif mode == 'chooseServer':
|
||||
entrypoint.choose_pms_server()
|
||||
|
||||
elif mode == 'refreshplaylist':
|
||||
log.info('Requesting playlist/nodes refresh')
|
||||
utils.plex_command('RUN_LIB_SCAN', 'views')
|
||||
|
||||
elif mode == 'deviceid':
|
||||
self.deviceid()
|
||||
|
||||
|
|
|
@ -1036,10 +1036,6 @@ msgctxt "#39201"
|
|||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#39203"
|
||||
msgid "Refresh Plex playlists/nodes"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#39204"
|
||||
msgid "Perform manual library sync"
|
||||
msgstr ""
|
||||
|
|
|
@ -170,8 +170,6 @@ def show_main_menu(content_type=None):
|
|||
|
||||
# some extra entries for settings and stuff
|
||||
directory_item(utils.lang(39201), "plugin://%s?mode=settings" % v.ADDON_ID)
|
||||
directory_item(utils.lang(39203),
|
||||
"plugin://%s?mode=refreshplaylist" % v.ADDON_ID)
|
||||
directory_item(utils.lang(39204),
|
||||
"plugin://%s?mode=manualsync" % v.ADDON_ID)
|
||||
xbmcplugin.endOfDirectory(int(argv[1]))
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from .full_sync import start, PLAYLIST_SYNC_ENABLED
|
||||
from .time import sync_pms_time
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
from logging import getLogger
|
||||
import time
|
||||
|
||||
from .get_metadata import GetMetadataTask
|
||||
from . import common, process_metadata, sections
|
||||
|
@ -24,12 +23,13 @@ LOG = getLogger('PLEX.library_sync.full_sync')
|
|||
|
||||
|
||||
class FullSync(backgroundthread.KillableThread, common.libsync_mixin):
|
||||
def __init__(self, repair, callback):
|
||||
def __init__(self, repair, callback, show_dialog):
|
||||
"""
|
||||
repair=True: force sync EVERY item
|
||||
"""
|
||||
self.repair = repair
|
||||
self.callback = callback
|
||||
self.show_dialog = show_dialog
|
||||
self.queue = None
|
||||
self.process_thread = None
|
||||
self.last_sync = None
|
||||
|
@ -141,14 +141,18 @@ class FullSync(backgroundthread.KillableThread, common.libsync_mixin):
|
|||
if self.isCanceled():
|
||||
return
|
||||
successful = False
|
||||
self.last_sync = time.time()
|
||||
self.last_sync = utils.unix_timestamp()
|
||||
# Delete playlist and video node files from Kodi
|
||||
utils.delete_playlists()
|
||||
utils.delete_nodes()
|
||||
# Get latest Plex libraries and build playlist and video node files
|
||||
if not sections.sync_from_pms():
|
||||
return
|
||||
try:
|
||||
# Fire up our single processing thread
|
||||
self.queue = backgroundthread.Queue.Queue(maxsize=200)
|
||||
self.processing_thread = process_metadata.ProcessMetadata(
|
||||
self.queue, self.last_sync)
|
||||
self.queue, self.last_sync, self.show_dialog)
|
||||
self.processing_thread.start()
|
||||
|
||||
# Actual syncing - do only new items first
|
||||
|
@ -180,12 +184,13 @@ class FullSync(backgroundthread.KillableThread, common.libsync_mixin):
|
|||
# This will block until the processing thread exits
|
||||
LOG.debug('Waiting for processing thread to exit')
|
||||
self.processing_thread.join()
|
||||
self.callback(successful)
|
||||
if self.callback:
|
||||
self.callback(successful)
|
||||
LOG.info('Done full_sync')
|
||||
|
||||
|
||||
def start(repair, callback):
|
||||
def start(show_dialog, repair=False, callback=None):
|
||||
"""
|
||||
"""
|
||||
# backgroundthread.BGThreader.addTask(FullSync().setup(repair, callback))
|
||||
FullSync(repair, callback).start()
|
||||
FullSync(repair, callback, show_dialog).start()
|
||||
|
|
|
@ -36,34 +36,38 @@ class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
|||
item_class: as used to call functions in itemtypes.py e.g. 'Movies' =>
|
||||
itemtypes.Movies()
|
||||
"""
|
||||
def __init__(self, queue, last_sync):
|
||||
def __init__(self, queue, last_sync, show_dialog):
|
||||
self.queue = queue
|
||||
self.last_sync = last_sync
|
||||
self.show_dialog = show_dialog
|
||||
self.total = 0
|
||||
self.current = 0
|
||||
self.title = None
|
||||
self.section_name = None
|
||||
self.dialog = None
|
||||
super(ProcessMetadata, self).__init__()
|
||||
|
||||
def update_dialog(self):
|
||||
def update(self):
|
||||
"""
|
||||
"""
|
||||
try:
|
||||
progress = int(float(self.current) / float(self.total) * 100.0)
|
||||
except ZeroDivisionError:
|
||||
progress = 0
|
||||
self.dialog.update(progress,
|
||||
self.section_name,
|
||||
'%s/%s: %s'
|
||||
% (self.current, self.total, self.title))
|
||||
if self.show_dialog:
|
||||
try:
|
||||
progress = int(float(self.current) / float(self.total) * 100.0)
|
||||
except ZeroDivisionError:
|
||||
progress = 0
|
||||
self.dialog.update(progress,
|
||||
self.section_name,
|
||||
'%s/%s: %s'
|
||||
% (self.current, self.total, self.title))
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Do the work
|
||||
"""
|
||||
LOG.debug('Processing thread started')
|
||||
self.dialog = xbmcgui.DialogProgressBG()
|
||||
self.dialog.create(utils.lang(39714))
|
||||
if self.show_dialog:
|
||||
self.dialog = xbmcgui.DialogProgressBG()
|
||||
self.dialog.create(utils.lang(39714))
|
||||
try:
|
||||
# Init with the very first library section. This will block!
|
||||
section = self.queue.get()
|
||||
|
@ -91,13 +95,15 @@ class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
|||
children=xml.children)
|
||||
except:
|
||||
utils.ERROR(txt='process_metadata crashed',
|
||||
notify=True)
|
||||
notify=True,
|
||||
cancel_sync=True)
|
||||
if self.current % 20 == 0:
|
||||
self.title = utils.cast(unicode,
|
||||
xml[0].get('title'))
|
||||
self.update_dialog()
|
||||
self.update()
|
||||
self.current += 1
|
||||
self.queue.task_done()
|
||||
finally:
|
||||
self.dialog.close()
|
||||
if self.dialog:
|
||||
self.dialog.close()
|
||||
LOG.debug('Processing thread terminated')
|
||||
|
|
108
resources/lib/library_sync/time.py
Normal file
108
resources/lib/library_sync/time.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
from logging import getLogger
|
||||
|
||||
import xbmc
|
||||
|
||||
from .. import plex_functions as PF, utils, variables as v, state
|
||||
|
||||
LOG = getLogger('PLEX.library_sync.time')
|
||||
|
||||
|
||||
def sync_pms_time():
|
||||
"""
|
||||
PMS does not provide a means to get a server timestamp. This is a work-
|
||||
around - because the PMS might be in another time zone
|
||||
|
||||
In general, everything saved to Kodi shall be in Kodi time.
|
||||
|
||||
Any info with a PMS timestamp is in Plex time, naturally
|
||||
"""
|
||||
LOG.info('Synching time with PMS server')
|
||||
# Find a PMS item where we can toggle the view state to enforce a
|
||||
# change in lastViewedAt
|
||||
|
||||
# Get all Plex libraries
|
||||
sections = PF.get_plex_sections()
|
||||
try:
|
||||
sections.attrib
|
||||
except AttributeError:
|
||||
LOG.error("Error download PMS views, abort sync_pms_time")
|
||||
return False
|
||||
|
||||
plex_id = None
|
||||
typus = (
|
||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_MOVIE,),
|
||||
(v.PLEX_TYPE_SHOW, v.PLEX_TYPE_EPISODE),
|
||||
(v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_SONG)
|
||||
)
|
||||
for section_type, plex_type in typus:
|
||||
if plex_id:
|
||||
break
|
||||
for section in sections:
|
||||
if plex_id:
|
||||
break
|
||||
if not section.attrib['type'] == section_type:
|
||||
continue
|
||||
library_id = section.attrib['key']
|
||||
try:
|
||||
iterator = PF.SectionItems(library_id, {'type': plex_type})
|
||||
for item in iterator:
|
||||
if item.get('viewCount'):
|
||||
# Don't want to mess with items that have playcount>0
|
||||
continue
|
||||
if item.get('viewOffset'):
|
||||
# Don't mess with items with a resume point
|
||||
continue
|
||||
plex_id = utils.cast(int, item.get('ratingKey'))
|
||||
LOG.info('Found a %s item to sync with: %s',
|
||||
plex_type, plex_id)
|
||||
break
|
||||
except RuntimeError:
|
||||
pass
|
||||
if plex_id is None:
|
||||
LOG.error("Could not find an item to sync time with")
|
||||
LOG.error("Aborting PMS-Kodi time sync")
|
||||
return False
|
||||
|
||||
# Get the Plex item's metadata
|
||||
xml = PF.GetPlexMetadata(plex_id)
|
||||
if xml in (None, 401):
|
||||
LOG.error("Could not download metadata, aborting time sync")
|
||||
return False
|
||||
|
||||
timestamp = xml[0].get('lastViewedAt')
|
||||
if timestamp is None:
|
||||
timestamp = xml[0].get('updatedAt')
|
||||
LOG.debug('Using items updatedAt=%s', timestamp)
|
||||
if timestamp is None:
|
||||
timestamp = xml[0].get('addedAt')
|
||||
LOG.debug('Using items addedAt=%s', timestamp)
|
||||
if timestamp is None:
|
||||
timestamp = 0
|
||||
LOG.debug('No timestamp; using 0')
|
||||
timestamp = utils.cast(int, timestamp)
|
||||
# Set the timer
|
||||
koditime = utils.unix_timestamp()
|
||||
# Toggle watched state
|
||||
PF.scrobble(plex_id, 'watched')
|
||||
# Let the PMS process this first!
|
||||
xbmc.sleep(1000)
|
||||
# Get updated metadata
|
||||
xml = PF.GetPlexMetadata(plex_id)
|
||||
# Toggle watched state back
|
||||
PF.scrobble(plex_id, 'unwatched')
|
||||
try:
|
||||
plextime = xml[0].get('lastViewedAt')
|
||||
except (IndexError, TypeError, AttributeError):
|
||||
LOG.error('Could not get lastViewedAt - aborting')
|
||||
return False
|
||||
|
||||
# Calculate time offset Kodi-PMS
|
||||
state.KODI_PLEX_TIME_OFFSET = float(koditime) - float(plextime)
|
||||
utils.settings('kodiplextimeoffset',
|
||||
value=str(state.KODI_PLEX_TIME_OFFSET))
|
||||
LOG.info("Time offset Koditime - Plextime in seconds: %s",
|
||||
state.KODI_PLEX_TIME_OFFSET)
|
||||
return True
|
File diff suppressed because it is too large
Load diff
|
@ -588,7 +588,7 @@ class SectionItems(DownloadGen):
|
|||
"""
|
||||
Iterator object to get all items of a Plex library section
|
||||
"""
|
||||
def __init__(self, section_id, args):
|
||||
def __init__(self, section_id, args=None):
|
||||
super(SectionItems, self).__init__(
|
||||
'{server}/library/sections/%s/all' % section_id, args)
|
||||
|
||||
|
|
|
@ -233,10 +233,12 @@ def dialog(typus, *args, **kwargs):
|
|||
return types[typus](*args, **kwargs)
|
||||
|
||||
|
||||
def ERROR(txt='', hide_tb=False, notify=False):
|
||||
def ERROR(txt='', hide_tb=False, notify=False, cancel_sync=False):
|
||||
import sys
|
||||
short = str(sys.exc_info()[1])
|
||||
LOG.error('Error encountered: %s - %s', txt, short)
|
||||
if cancel_sync:
|
||||
state.STOP_SYNC = True
|
||||
if hide_tb:
|
||||
return short
|
||||
|
||||
|
|
Loading…
Reference in a new issue