Merge pull request #958 from croneter/beta-version

Bump master
This commit is contained in:
croneter 2019-08-03 10:44:35 +02:00 committed by GitHub
commit b5e13d0ab6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 119 additions and 63 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-2.9.1-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.9.1-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
[![stable version](https://img.shields.io/badge/stable_version-2.9.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.9.3-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)
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.9.1" provider-name="croneter">
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.9.3" provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" />
@ -83,7 +83,21 @@
<summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary>
<description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description>
<disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer>
<news>version 2.9.1:
<news>version 2.9.3:
- version 2.9.2 for everyone
version 2.9.2 (beta only):
- Fix Plex Companion casting from iOS and Android
- Faster sync of playlists
- Sync playlists immediately after synching new/changed items and show an info dialog
- Fix potential playlist sync issues if there is a dot in the playlist name
- Correctly detect whether we already synched a Kodi playlist
- Remove obsolete check if path is indeed in unicode
- Add unicode representation to Playlist() class
- Separate function to wipe all synched Plex playlists
- Less logging when comparing PKC versions
version 2.9.1:
- Fix On Deck and Recently Added Episodes for shows not appending showname and season and episode number
version 2.9.0:

View file

@ -1,3 +1,17 @@
version 2.9.3:
- version 2.9.2 for everyone
version 2.9.2 (beta only):
- Fix Plex Companion casting from iOS and Android
- Faster sync of playlists
- Sync playlists immediately after synching new/changed items and show an info dialog
- Fix potential playlist sync issues if there is a dot in the playlist name
- Correctly detect whether we already synched a Kodi playlist
- Remove obsolete check if path is indeed in unicode
- Add unicode representation to Playlist() class
- Separate function to wipe all synched Plex playlists
- Less logging when comparing PKC versions
version 2.9.1:
- Fix On Deck and Recently Added Episodes for shows not appending showname and season and episode number

View file

@ -334,15 +334,27 @@ class FullSync(common.fullsync_mixin):
plexdb.update_section_last_sync(section.section_id,
self.current_sync)
common.update_kodi_library(video=True, music=True)
# Sync Plex playlists to Kodi and vice-versa
if common.PLAYLIST_SYNC_ENABLED:
if self.show_dialog:
if self.dialog:
self.dialog.close()
self.dialog = xbmcgui.DialogProgressBG()
# "Synching playlists"
self.dialog.create(utils.lang(39715))
if not playlists.full_sync():
return False
# SYNC PLAYSTATE of ALL items (otherwise we won't pick up on items that
# were set to unwatched). Also mark all items on the PMS to be able
# to delete the ones still in Kodi
LOG.info('Start synching playstate and userdata for every item')
# In order to not delete all your songs again
if app.SYNC.enable_music:
kinds.extend([
(v.PLEX_TYPE_SONG, v.PLEX_TYPE_ARTIST, itemtypes.Song, True),
])
# SYNC PLAYSTATE of ALL items (otherwise we won't pick up on items that
# were set to unwatched). Also mark all items on the PMS to be able
# to delete the ones still in Kodi
LOG.info('Start synching playstate and userdata for every item')
# Make sure we're not showing an item's title in the sync dialog
self.title = ''
self.threader.shutdown()
@ -428,13 +440,6 @@ class FullSync(common.fullsync_mixin):
if self.isCanceled() or not self.full_library_sync():
self.successful = False
return
if common.PLAYLIST_SYNC_ENABLED:
if self.dialog:
self.dialog.close()
self.dialog = xbmcgui.DialogProgressBG()
self.dialog.create(utils.lang(39715))
if not playlists.full_sync():
self.successful = False
finally:
common.update_kodi_library(video=True, music=True)
if self.dialog:

View file

@ -57,4 +57,9 @@ def check_migration():
sections.clear_window_vars()
sections.delete_videonode_files()
if not utils.compare_version(last_migration, '2.9.3'):
LOG.info('Migrating to version 2.9.2')
# Re-sync all playlists to Kodi
utils.wipe_synched_playlists()
utils.settings('last_migrated_PKC_version', value=v.ADDON_VERSION)

View file

@ -14,7 +14,8 @@
from __future__ import absolute_import, division, unicode_literals
from logging import getLogger
from .common import Playlist, PlaylistError, PlaylistObserver
from .common import Playlist, PlaylistError, PlaylistObserver, \
kodi_playlist_hash
from . import pms, db, kodi_pl, plex_pl
from ..watchdog import events
@ -221,7 +222,7 @@ def _full_sync():
pass
if not sync_kodi_playlist(path):
continue
kodi_hash = utils.generate_file_md5(path)
kodi_hash = kodi_playlist_hash(path)
playlist = db.get_playlist(path=path)
if playlist and playlist.kodi_hash == kodi_hash:
continue
@ -387,7 +388,7 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
def on_created(self, event):
LOG.debug('on_created: %s', event.src_path)
old_playlist = db.get_playlist(path=event.src_path)
kodi_hash = utils.generate_file_md5(event.src_path)
kodi_hash = kodi_playlist_hash(event.src_path)
if old_playlist and old_playlist.kodi_hash == kodi_hash:
LOG.debug('Playlist already in DB - skipping')
return
@ -406,7 +407,7 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
def on_modified(self, event):
LOG.debug('on_modified: %s', event.src_path)
old_playlist = db.get_playlist(path=event.src_path)
kodi_hash = utils.generate_file_md5(event.src_path)
kodi_hash = kodi_playlist_hash(event.src_path)
if old_playlist and old_playlist.kodi_hash == kodi_hash:
LOG.debug('Nothing modified, playlist already in DB - skipping')
return
@ -425,7 +426,7 @@ class PlaylistEventhandler(events.FileSystemEventHandler):
def on_moved(self, event):
LOG.debug('on_moved: %s to %s', event.src_path, event.dest_path)
kodi_hash = utils.generate_file_md5(event.dest_path)
kodi_hash = kodi_playlist_hash(event.dest_path)
# First check whether we don't already have destination playlist in
# our DB. Just in case....
old_playlist = db.get_playlist(path=event.dest_path)

View file

@ -4,6 +4,8 @@ from __future__ import absolute_import, division, unicode_literals
from logging import getLogger
import Queue
import time
import os
import hashlib
from ..watchdog import events
from ..watchdog.observers import Observer
@ -63,7 +65,7 @@ class Playlist(object):
self.kodi_type = None
self.kodi_hash = None
def __repr__(self):
def __unicode__(self):
return ("{{"
"'plex_id': {self.plex_id}, "
"'plex_name': '{self.plex_name}', "
@ -74,6 +76,9 @@ class Playlist(object):
"'kodi_hash': '{self.kodi_hash}'"
"}}").format(self=self)
def __repr__(self):
return self.__unicode__().encode('utf-8')
def __str__(self):
return self.__repr__()
@ -101,11 +106,9 @@ class Playlist(object):
@kodi_path.setter
def kodi_path(self, path):
if not isinstance(path, unicode):
raise RuntimeError('Path not in unicode: %s' % path)
f = path_ops.path.basename(path)
try:
self.kodi_filename, self.kodi_extension = f.split('.', 1)
self.kodi_filename, self.kodi_extension = f.rsplit('.', 1)
except ValueError:
LOG.error('Trying to set invalid path: %s', path)
raise PlaylistError('Invalid path: %s' % path)
@ -121,6 +124,22 @@ class Playlist(object):
self._kodi_path = path
def kodi_playlist_hash(path):
"""
Returns a md5 hash [unicode] using os.stat() st_size and st_mtime for the
playlist located at path [unicode]
(size of file in bytes and time of most recent content modification)
There are probably way more efficient ways out there to do this
"""
stat = os.stat(path_ops.encode_path(path))
# stat.st_size is of type long; stat.st_mtime is of type float - hash both
m = hashlib.md5()
m.update(repr(stat.st_size))
m.update(repr(stat.st_mtime))
return m.hexdigest().decode('utf-8')
class PlaylistQueue(OrderedSetQueue):
"""
OrderedSetQueue that drops all directory events immediately

View file

@ -7,7 +7,7 @@ from __future__ import absolute_import, division, unicode_literals
from logging import getLogger
import re
from .common import Playlist, PlaylistError
from .common import Playlist, PlaylistError, kodi_playlist_hash
from . import db, pms
from ..plex_api import API
@ -71,7 +71,7 @@ def create(plex_id):
except Exception:
IGNORE_KODI_PLAYLIST_CHANGE.remove(playlist.kodi_path)
raise
playlist.kodi_hash = utils.generate_file_md5(path)
playlist.kodi_hash = kodi_playlist_hash(path)
db.update_playlist(playlist)
LOG.debug('Created Kodi playlist based on Plex playlist: %s', playlist)

View file

@ -30,8 +30,8 @@ def create(playlist):
if not plex_ids:
LOG.warning('No Plex ids found for playlist %s', playlist)
raise PlaylistError
IGNORE_PLEX_PLAYLIST_CHANGE.append(playlist.plex_id)
pms.add_items(playlist, plex_ids)
IGNORE_PLEX_PLAYLIST_CHANGE.append(playlist.plex_id)
db.update_playlist(playlist)
LOG.debug('Done creating Plex playlist %s', playlist)

View file

@ -167,7 +167,7 @@ class PlexCompanion(backgroundthread.KillableThread):
repeat=query.get('repeat'),
offset=data.get('offset'),
transient_token=data.get('token'),
key=key)
start_plex_id=key)
@staticmethod
def _process_streams(data):

View file

@ -311,11 +311,15 @@ def initialize():
plexdb.cursor.execute(cmd)
def wipe():
def wipe(table=None):
"""
Completely resets the Plex database
Completely resets the Plex database.
If a table [unicode] name is provided, only that table will be dropped
"""
with PlexDBBase() as plexdb:
if table:
tables = [table]
else:
plexdb.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
tables = [i[0] for i in plexdb.cursor.fetchall()]
for table in tables:

View file

@ -17,7 +17,6 @@ import xml.etree.ElementTree as etree
from . import defused_etree
from xml.etree.ElementTree import ParseError
from functools import wraps
import hashlib
import re
import gc
try:
@ -539,6 +538,29 @@ def create_kodi_db_indicees():
conn.close()
def wipe_synched_playlists():
"""
Deletes all synched playlist files on the Kodi side; resets the Plex table
listing all synched Plex playlists
"""
from . import plex_db
try:
with plex_db.PlexDB() as plexdb:
plexdb.cursor.execute('SELECT kodi_path FROM playlists')
playlist_paths = [x[0] for x in plexdb.cursor]
except OperationalError:
# Plex DB completely empty yet
playlist_paths = []
for path in playlist_paths:
try:
path_ops.remove(path)
LOG.info('Removed playlist %s', path)
except (OSError, IOError):
LOG.warn('Could not remove playlist %s', path)
# Now wipe our database
plex_db.wipe(table='playlists')
def wipe_database(reboot=True):
"""
Deletes all Plex playlists as well as video nodes, then clears Kodi as well
@ -550,8 +572,8 @@ def wipe_database(reboot=True):
from . import kodi_db, plex_db
# Clean up the playlists and video nodes
delete_files()
# First get the paths to all synced playlists
playlist_paths = []
# Wipe all synched playlists
wipe_synched_playlists()
try:
with plex_db.PlexDB() as plexdb:
if plexdb.songs_have_been_synced():
@ -560,22 +582,12 @@ def wipe_database(reboot=True):
else:
LOG.info('No music has been synced in the past - not wiping')
music = False
plexdb.cursor.execute('SELECT kodi_path FROM playlists')
for entry in plexdb.cursor:
playlist_paths.append(entry[0])
except OperationalError:
# Plex DB completely empty yet. Wipe existing Kodi music only if we
# expect to sync Plex music
music = settings('enableMusic') == 'true'
kodi_db.wipe_dbs(music)
plex_db.wipe()
# Delete all synced playlists
for path in playlist_paths:
try:
path_ops.remove(path)
LOG.debug('Removed playlist %s', path)
except (OSError, IOError):
LOG.warn('Could not remove playlist %s', path)
LOG.info("Resetting all cached artwork.")
# Remove all cached artwork
@ -639,7 +651,6 @@ def compare_version(current, minimum):
Input strings: e.g. "1.2.3"; always with Major, Minor and Patch!
"""
LOG.info("current DB: %s minimum DB: %s", current, minimum)
try:
curr_major, curr_minor, curr_patch = current.split(".")
except ValueError:
@ -931,23 +942,6 @@ class XmlKodiSetting(object):
return element
def generate_file_md5(path):
"""
Generates the md5 hash value for the file located at path [unicode].
The hash does not include the path and filename and is thus identical for
a file that was moved/changed name.
Returns a unique unicode containing only hexadecimal digits
"""
m = hashlib.md5()
with open(path_ops.encode_path(path), 'rb') as f:
while True:
piece = f.read(32768)
if not piece:
break
m.update(piece)
return m.hexdigest().decode('utf-8')
def process_method_on_list(method_to_run, items):
"""
helper method that processes a method on each item with pooling if the