From e52b67c3a94c2edc48e496bd8271b9461ac190b1 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 15 Nov 2019 08:24:21 +0100 Subject: [PATCH] Fix to correctly wipe Kodi databases --- resources/lib/db.py | 16 ++++++++++------ resources/lib/kodi_db/__init__.py | 27 ++++++++++++++++----------- resources/lib/kodi_db/common.py | 11 ++++++++--- resources/lib/kodi_db/texture.py | 9 --------- resources/lib/utils.py | 4 +--- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/resources/lib/db.py b/resources/lib/db.py index fc0363c9..30f91315 100644 --- a/resources/lib/db.py +++ b/resources/lib/db.py @@ -52,21 +52,25 @@ def catch_operationalerrors(method): return wrapper -def _initial_db_connection_setup(conn): +def _initial_db_connection_setup(conn, wal_mode): """ Set-up DB e.g. for WAL journal mode, if that hasn't already been done before. Also start a transaction """ - conn.execute('PRAGMA journal_mode=WAL;') - conn.execute('PRAGMA cache_size = -8000;') - conn.execute('PRAGMA synchronous=NORMAL;') + if wal_mode: + conn.execute('PRAGMA journal_mode=WAL;') + conn.execute('PRAGMA cache_size = -8000;') + conn.execute('PRAGMA synchronous=NORMAL;') conn.execute('BEGIN') -def connect(media_type=None): +def connect(media_type=None, wal_mode=True): """ Open a connection to the Kodi database. media_type: 'video' (standard if not passed), 'plex', 'music', 'texture' + Pass wal_mode=False if you want the standard (and slower) sqlite + journal_mode, e.g. when wiping entire tables. Useful if you do NOT want + concurrent access to DB for both PKC and Kodi """ if media_type == "plex": db_path = v.DB_PLEX_PATH @@ -80,7 +84,7 @@ def connect(media_type=None): attempts = DB_WRITE_ATTEMPTS while True: try: - _initial_db_connection_setup(conn) + _initial_db_connection_setup(conn, wal_mode) except sqlite3.OperationalError as err: if 'database is locked' not in err: # Not an error we want to catch, so reraise it diff --git a/resources/lib/kodi_db/__init__.py b/resources/lib/kodi_db/__init__.py index 67e24cf7..200d83ed 100644 --- a/resources/lib/kodi_db/__init__.py +++ b/resources/lib/kodi_db/__init__.py @@ -61,7 +61,10 @@ def setup_kodi_default_entries(): def reset_cached_images(): LOG.info('Resetting cached artwork') - # Remove all existing textures first + LOG.debug('Resetting the Kodi texture DB') + with KodiTextureDB(wal_mode=False) as kodidb: + kodidb.wipe() + LOG.debug('Deleting all cached image files') path = path_ops.translate_path('special://thumbnails/') if path_ops.exists(path): path_ops.rmtree(path, ignore_errors=True) @@ -72,10 +75,10 @@ def reset_cached_images(): new_path = path_ops.translate_path('special://thumbnails/%s' % path) try: path_ops.makedirs(path_ops.encode_path(new_path)) - except OSError: - pass - with KodiTextureDB() as kodidb: - kodidb.reset_cached_images() + except OSError as err: + LOG.warn('Could not create thumbnail directory %s: %s', + new_path, err) + LOG.info('Done resetting cached artwork') def wipe_dbs(music=True): @@ -83,16 +86,18 @@ def wipe_dbs(music=True): Completely resets the Kodi databases 'video', 'texture' and 'music' (if music sync is enabled) - DO NOT use context menu as we need to connect without WAL mode - if Kodi - is still accessing the DB + We need to connect without sqlite WAL mode as Kodi might still be accessing + the dbs and we need to prevent that """ LOG.warn('Wiping Kodi databases!') - kinds = [KodiVideoDB, KodiTextureDB] + LOG.info('Wiping Kodi video database') + with KodiVideoDB(wal_mode=False) as kodidb: + kodidb.wipe() if music: - kinds.insert(1, KodiMusicDB) - for kind in kinds: - with kind() as kodidb: + LOG.info('Wiping Kodi music database') + with KodiMusicDB(wal_mode=False) as kodidb: kodidb.wipe() + reset_cached_images() setup_kodi_default_entries() # Delete SQLITE wal files import xbmc diff --git a/resources/lib/kodi_db/common.py b/resources/lib/kodi_db/common.py index a7ddb4d6..2a331290 100644 --- a/resources/lib/kodi_db/common.py +++ b/resources/lib/kodi_db/common.py @@ -14,9 +14,12 @@ class KodiDBBase(object): """ Kodi database methods used for all types of items """ - def __init__(self, texture_db=False, kodiconn=None, artconn=None, lock=True): + def __init__(self, texture_db=False, kodiconn=None, artconn=None, + lock=True, wal_mode=True): """ Allows direct use with a cursor instead of context mgr + Pass wal_mode=False if you want the standard sqlite journal_mode, e.g. + when wiping entire tables """ self._texture_db = texture_db self.lock = lock @@ -24,13 +27,15 @@ class KodiDBBase(object): self.cursor = self.kodiconn.cursor() if self.kodiconn else None self.artconn = artconn self.artcursor = self.artconn.cursor() if self.artconn else None + self.wal_mode = wal_mode def __enter__(self): if self.lock: KODIDB_LOCK.acquire() - self.kodiconn = db.connect(self.db_kind) + self.kodiconn = db.connect(self.db_kind, self.wal_mode) self.cursor = self.kodiconn.cursor() - self.artconn = db.connect('texture') if self._texture_db else None + self.artconn = db.connect('texture', self.wal_mode) if self._texture_db \ + else None self.artcursor = self.artconn.cursor() if self._texture_db else None return self diff --git a/resources/lib/kodi_db/texture.py b/resources/lib/kodi_db/texture.py index 12600950..14e016f2 100644 --- a/resources/lib/kodi_db/texture.py +++ b/resources/lib/kodi_db/texture.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, division, unicode_literals from . import common -from .. import db class KodiTextureDB(common.KodiDBBase): @@ -16,11 +15,3 @@ class KodiTextureDB(common.KodiDBBase): self.artcursor.execute('SELECT url FROM texture WHERE url = ? LIMIT 1', (url, )) return self.artcursor.fetchone() is None - - @db.catch_operationalerrors - def reset_cached_images(self): - for row in self.cursor.execute('SELECT tbl_name ' - 'FROM sqlite_master WHERE type=?', - ('table', )): - if row[0] != 'version': - self.cursor.execute("DELETE FROM %s" % row[0]) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index ab2a0eaf..0b025128 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -573,12 +573,10 @@ def wipe_database(reboot=True): # Plex DB completely empty yet. Wipe existing Kodi music only if we # expect to sync Plex music music = settings('enableMusic') == 'true' + LOG.info("Resetting all cached artwork.") kodi_db.wipe_dbs(music) plex_db.wipe() - LOG.info("Resetting all cached artwork.") - # Remove all cached artwork - kodi_db.reset_cached_images() # reset the install run flag settings('SyncInstallRunDone', value="false") settings('sections_asked_for_machine_identifier', value='')