Merge branch 'beta-version'
This commit is contained in:
commit
5926168427
6 changed files with 102 additions and 15 deletions
|
@ -1,5 +1,5 @@
|
||||||
[![stable version](https://img.shields.io/badge/stable_version-2.1.3-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
|
[![stable version](https://img.shields.io/badge/stable_version-2.1.3-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
|
||||||
[![beta version](https://img.shields.io/badge/beta_version-2.2.8-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
[![beta version](https://img.shields.io/badge/beta_version-2.2.9-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
||||||
|
|
||||||
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
||||||
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
||||||
|
|
10
addon.xml
10
addon.xml
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.2.8" provider-name="croneter">
|
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.2.9" provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
<import addon="script.module.requests" version="2.9.1" />
|
<import addon="script.module.requests" version="2.9.1" />
|
||||||
|
@ -73,7 +73,13 @@
|
||||||
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
|
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
|
||||||
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
|
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
|
||||||
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
|
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
|
||||||
<news>version 2.2.8 (beta only):
|
<news>version 2.2.9 (beta only):
|
||||||
|
- Hopefully fix Kodi and Plex playlists getting out of sync
|
||||||
|
- Fix and optimize startup of playlist sync
|
||||||
|
- Hide certain playlist settings under certain conditions
|
||||||
|
- Fix errors in Kodi log
|
||||||
|
|
||||||
|
version 2.2.8 (beta only):
|
||||||
- Support for Plex collection artwork (PKC settings toggle under Artwork)
|
- Support for Plex collection artwork (PKC settings toggle under Artwork)
|
||||||
- Fix hard PKC reset not working (OSError: no such file)
|
- Fix hard PKC reset not working (OSError: no such file)
|
||||||
- Deduplication
|
- Deduplication
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
version 2.2.9 (beta only):
|
||||||
|
- Hopefully fix Kodi and Plex playlists getting out of sync
|
||||||
|
- Fix and optimize startup of playlist sync
|
||||||
|
- Hide certain playlist settings under certain conditions
|
||||||
|
- Fix errors in Kodi log
|
||||||
|
|
||||||
version 2.2.8 (beta only):
|
version 2.2.8 (beta only):
|
||||||
- Support for Plex collection artwork (PKC settings toggle under Artwork)
|
- Support for Plex collection artwork (PKC settings toggle under Artwork)
|
||||||
- Fix hard PKC reset not working (OSError: no such file)
|
- Fix hard PKC reset not working (OSError: no such file)
|
||||||
|
|
|
@ -262,9 +262,10 @@ class LibrarySync(Thread):
|
||||||
# This will NOT update playstates and userratings!
|
# This will NOT update playstates and userratings!
|
||||||
LOG.info('Running fullsync for CHANGED PMS items with repair=%s',
|
LOG.info('Running fullsync for CHANGED PMS items with repair=%s',
|
||||||
repair)
|
repair)
|
||||||
if self._full_sync() is False:
|
if not self._full_sync():
|
||||||
|
return False
|
||||||
|
if not playlists.full_sync():
|
||||||
return False
|
return False
|
||||||
playlists.full_sync()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _full_sync(self):
|
def _full_sync(self):
|
||||||
|
@ -1265,8 +1266,8 @@ class LibrarySync(Thread):
|
||||||
"""
|
"""
|
||||||
for item in data:
|
for item in data:
|
||||||
status = item['state']
|
status = item['state']
|
||||||
if status == 'buffering':
|
if status == 'buffering' or status == 'stopped':
|
||||||
# Drop buffering messages immediately
|
# Drop buffering and stop messages immediately - no value
|
||||||
continue
|
continue
|
||||||
plex_id = item['ratingKey']
|
plex_id = item['ratingKey']
|
||||||
skip = False
|
skip = False
|
||||||
|
@ -1561,10 +1562,9 @@ class LibrarySync(Thread):
|
||||||
initial_sync_done = True
|
initial_sync_done = True
|
||||||
kodi_db_version_checked = True
|
kodi_db_version_checked = True
|
||||||
last_sync = utils.unix_timestamp()
|
last_sync = utils.unix_timestamp()
|
||||||
|
playlist_monitor = playlists.kodi_playlist_monitor()
|
||||||
self.sync_fanart()
|
self.sync_fanart()
|
||||||
self.fanartthread.start()
|
self.fanartthread.start()
|
||||||
if state.SYNC_PLAYLISTS and playlists.full_sync():
|
|
||||||
playlist_monitor = playlists.kodi_playlist_monitor()
|
|
||||||
else:
|
else:
|
||||||
LOG.error('Initial start-up full sync unsuccessful')
|
LOG.error('Initial start-up full sync unsuccessful')
|
||||||
xbmc.executebuiltin('InhibitIdleShutdown(false)')
|
xbmc.executebuiltin('InhibitIdleShutdown(false)')
|
||||||
|
@ -1614,11 +1614,10 @@ class LibrarySync(Thread):
|
||||||
initial_sync_done = True
|
initial_sync_done = True
|
||||||
last_sync = utils.unix_timestamp()
|
last_sync = utils.unix_timestamp()
|
||||||
LOG.info('Done initial sync on Kodi startup')
|
LOG.info('Done initial sync on Kodi startup')
|
||||||
|
playlist_monitor = playlists.kodi_playlist_monitor()
|
||||||
artwork.Artwork().cache_major_artwork()
|
artwork.Artwork().cache_major_artwork()
|
||||||
self.sync_fanart()
|
self.sync_fanart()
|
||||||
self.fanartthread.start()
|
self.fanartthread.start()
|
||||||
if state.SYNC_PLAYLISTS and playlists.full_sync():
|
|
||||||
playlist_monitor = playlists.kodi_playlist_monitor()
|
|
||||||
else:
|
else:
|
||||||
LOG.info('Startup sync has not yet been successful')
|
LOG.info('Startup sync has not yet been successful')
|
||||||
utils.window('plex_dbScan', clear=True)
|
utils.window('plex_dbScan', clear=True)
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
import Queue
|
||||||
|
import xbmc
|
||||||
|
|
||||||
from .watchdog import events
|
from .watchdog import events
|
||||||
from .watchdog.observers import Observer
|
from .watchdog.observers import Observer
|
||||||
|
from .watchdog.utils.bricks import OrderedSetQueue
|
||||||
from . import playlist_func as PL
|
from . import playlist_func as PL
|
||||||
from .plex_api import API
|
from .plex_api import API
|
||||||
from . import kodidb_functions as kodidb
|
from . import kodidb_functions as kodidb
|
||||||
|
@ -16,6 +19,11 @@ from . import state
|
||||||
|
|
||||||
LOG = getLogger('PLEX.playlists')
|
LOG = getLogger('PLEX.playlists')
|
||||||
|
|
||||||
|
# Safety margin for playlist filesystem operations
|
||||||
|
FILESYSTEM_TIMEOUT = 3
|
||||||
|
# These filesystem events are considered similar
|
||||||
|
SIMILAR_EVENTS = (events.EVENT_TYPE_CREATED, events.EVENT_TYPE_MODIFIED)
|
||||||
|
|
||||||
# Which playlist formates are supported by PKC?
|
# Which playlist formates are supported by PKC?
|
||||||
SUPPORTED_FILETYPES = (
|
SUPPORTED_FILETYPES = (
|
||||||
'm3u',
|
'm3u',
|
||||||
|
@ -329,6 +337,9 @@ def full_sync():
|
||||||
Full sync of playlists between Kodi and Plex. Returns True is successful,
|
Full sync of playlists between Kodi and Plex. Returns True is successful,
|
||||||
False otherwise
|
False otherwise
|
||||||
"""
|
"""
|
||||||
|
if not state.SYNC_PLAYLISTS:
|
||||||
|
LOG.debug('Not syncing playlists')
|
||||||
|
return True
|
||||||
LOG.info('Starting playlist full sync')
|
LOG.info('Starting playlist full sync')
|
||||||
with state.LOCK_PLAYLISTS:
|
with state.LOCK_PLAYLISTS:
|
||||||
return _full_sync()
|
return _full_sync()
|
||||||
|
@ -467,6 +478,9 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
|
||||||
:type event:
|
:type event:
|
||||||
:class:`FileSystemEvent`
|
:class:`FileSystemEvent`
|
||||||
"""
|
"""
|
||||||
|
if not state.SYNC_PLAYLISTS:
|
||||||
|
# Sync is deactivated
|
||||||
|
return
|
||||||
if event.is_directory:
|
if event.is_directory:
|
||||||
# todo: take care of folder renames
|
# todo: take care of folder renames
|
||||||
return
|
return
|
||||||
|
@ -545,7 +559,7 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
|
||||||
LOG.debug('on_moved: %s to %s', event.src_path, event.dest_path)
|
LOG.debug('on_moved: %s to %s', event.src_path, event.dest_path)
|
||||||
old_playlist = playlist_object_from_db(path=event.src_path)
|
old_playlist = playlist_object_from_db(path=event.src_path)
|
||||||
if not old_playlist:
|
if not old_playlist:
|
||||||
LOG.error('Did not have source path in the DB', event.src_path)
|
LOG.error('Did not have source path in the DB %s', event.src_path)
|
||||||
else:
|
else:
|
||||||
delete_plex_playlist(old_playlist)
|
delete_plex_playlist(old_playlist)
|
||||||
new_playlist = PL.Playlist_Object()
|
new_playlist = PL.Playlist_Object()
|
||||||
|
@ -557,6 +571,68 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistObserver(Observer):
|
||||||
|
"""
|
||||||
|
PKC implementation, overriding the dispatcher. PKC will wait for the
|
||||||
|
duration timeout (in seconds) before dispatching. A new event will reset
|
||||||
|
the timer.
|
||||||
|
Creating and modifying will be regarded as equal.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PlaylistObserver, self).__init__(*args, **kwargs)
|
||||||
|
# Drop the same events that get into the queue even if there are other
|
||||||
|
# events in between these similar events
|
||||||
|
self._event_queue = OrderedSetQueue()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _pkc_similar_events(event1, event2):
|
||||||
|
if event1 == event2:
|
||||||
|
return True
|
||||||
|
elif (event1.src_path == event2.src_path and
|
||||||
|
event1.event_type in SIMILAR_EVENTS and
|
||||||
|
event2.event_type in SIMILAR_EVENTS):
|
||||||
|
# Ignore a consecutive firing of created and modified events
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _dispatch_iterator(self, event_queue, timeout):
|
||||||
|
"""
|
||||||
|
This iterator will block for timeout (seconds) until an event is
|
||||||
|
received or raise Queue.Empty.
|
||||||
|
"""
|
||||||
|
event, watch = event_queue.get(block=True, timeout=timeout)
|
||||||
|
event_queue.task_done()
|
||||||
|
start = utils.unix_timestamp()
|
||||||
|
while utils.unix_timestamp() - start < timeout:
|
||||||
|
if state.STOP_PKC:
|
||||||
|
raise Queue.Empty
|
||||||
|
try:
|
||||||
|
new_event, new_watch = event_queue.get(block=False)
|
||||||
|
except Queue.Empty:
|
||||||
|
xbmc.sleep(200)
|
||||||
|
else:
|
||||||
|
event_queue.task_done()
|
||||||
|
start = utils.unix_timestamp()
|
||||||
|
if self._pkc_similar_events(new_event, event):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# At least on Windows, a dir modified event will be
|
||||||
|
# triggered once the writing process is done. Fine though
|
||||||
|
yield event, watch
|
||||||
|
event, watch = new_event, new_watch
|
||||||
|
yield event, watch
|
||||||
|
|
||||||
|
def dispatch_events(self, event_queue, timeout):
|
||||||
|
for event, watch in self._dispatch_iterator(event_queue, timeout):
|
||||||
|
with self._lock:
|
||||||
|
# To allow unschedule/stop and safe removal of event handlers
|
||||||
|
# within event handlers itself, check if the handler is still
|
||||||
|
# registered after every dispatch.
|
||||||
|
for handler in list(self._handlers.get(watch, [])):
|
||||||
|
if handler in self._handlers.get(watch, []):
|
||||||
|
handler.dispatch(event)
|
||||||
|
|
||||||
|
|
||||||
def kodi_playlist_monitor():
|
def kodi_playlist_monitor():
|
||||||
"""
|
"""
|
||||||
Monitors the Kodi playlist folder special://profile/playlist for the user.
|
Monitors the Kodi playlist folder special://profile/playlist for the user.
|
||||||
|
@ -566,7 +642,7 @@ def kodi_playlist_monitor():
|
||||||
observer.stop() (and maybe observer.join()) to shut down properly
|
observer.stop() (and maybe observer.join()) to shut down properly
|
||||||
"""
|
"""
|
||||||
event_handler = PlaylistEventhandler()
|
event_handler = PlaylistEventhandler()
|
||||||
observer = Observer()
|
observer = PlaylistObserver(timeout=FILESYSTEM_TIMEOUT)
|
||||||
observer.schedule(event_handler, v.PLAYLIST_PATH, recursive=True)
|
observer.schedule(event_handler, v.PLAYLIST_PATH, recursive=True)
|
||||||
observer.start()
|
observer.start()
|
||||||
return observer
|
return observer
|
||||||
|
|
|
@ -65,9 +65,9 @@
|
||||||
<setting type="lsep" label="$LOCALIZE[136]" /><!-- Playlists -->
|
<setting type="lsep" label="$LOCALIZE[136]" /><!-- Playlists -->
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="enablePlaylistSync" type="bool" label="30020" default="true" visible="true"/><!-- Sync Plex playlists -->
|
<setting id="enablePlaylistSync" type="bool" label="30020" default="true" visible="true"/><!-- Sync Plex playlists -->
|
||||||
<setting id="syncSpecificKodiPlaylists" type="bool" label="30023" default="false" visible="true"/><!-- Only sync specific Kodi playlists to Plex -->
|
<setting id="syncSpecificKodiPlaylists" type="bool" label="30023" default="false" visible="eq(-1,true)" /><!-- Only sync specific Kodi playlists to Plex -->
|
||||||
<setting id="syncSpecificKodiPlaylistsPrefix" type="text" label="30027" default="sync_" visible="eq(-1,true)"/><!-- Prefix in Kodi playlist name to trigger sync -->
|
<setting id="syncSpecificKodiPlaylistsPrefix" type="text" label="30027" default="sync_" visible="eq(-1,true)"/><!-- Prefix in Kodi playlist name to trigger sync -->
|
||||||
<setting id="syncSpecificPlexPlaylists" type="bool" label="30021" default="false" visible="true"/><!-- Only sync specific Plex playlists to Kodi -->
|
<setting id="syncSpecificPlexPlaylists" type="bool" label="30021" default="false" visible="eq(-3,true)"/><!-- Only sync specific Plex playlists to Kodi -->
|
||||||
<setting id="syncSpecificPlexPlaylistsPrefix" type="text" label="30026" default="sync_" visible="eq(-1,true)" /><!-- Prefix in Plex playlist name to trigger sync -->
|
<setting id="syncSpecificPlexPlaylistsPrefix" type="text" label="30026" default="sync_" visible="eq(-1,true)" /><!-- Prefix in Plex playlist name to trigger sync -->
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting type="lsep" label="39052" /><!-- Background Sync -->
|
<setting type="lsep" label="39052" /><!-- Background Sync -->
|
||||||
|
|
Loading…
Reference in a new issue