Fix to correctly wipe Kodi databases

This commit is contained in:
croneter 2019-11-15 08:24:21 +01:00
parent 19a964ccb2
commit e52b67c3a9
5 changed files with 35 additions and 32 deletions

View file

@ -52,21 +52,25 @@ def catch_operationalerrors(method):
return wrapper 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 Set-up DB e.g. for WAL journal mode, if that hasn't already been done
before. Also start a transaction before. Also start a transaction
""" """
conn.execute('PRAGMA journal_mode=WAL;') if wal_mode:
conn.execute('PRAGMA cache_size = -8000;') conn.execute('PRAGMA journal_mode=WAL;')
conn.execute('PRAGMA synchronous=NORMAL;') conn.execute('PRAGMA cache_size = -8000;')
conn.execute('PRAGMA synchronous=NORMAL;')
conn.execute('BEGIN') conn.execute('BEGIN')
def connect(media_type=None): def connect(media_type=None, wal_mode=True):
""" """
Open a connection to the Kodi database. Open a connection to the Kodi database.
media_type: 'video' (standard if not passed), 'plex', 'music', 'texture' 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": if media_type == "plex":
db_path = v.DB_PLEX_PATH db_path = v.DB_PLEX_PATH
@ -80,7 +84,7 @@ def connect(media_type=None):
attempts = DB_WRITE_ATTEMPTS attempts = DB_WRITE_ATTEMPTS
while True: while True:
try: try:
_initial_db_connection_setup(conn) _initial_db_connection_setup(conn, wal_mode)
except sqlite3.OperationalError as err: except sqlite3.OperationalError as err:
if 'database is locked' not in err: if 'database is locked' not in err:
# Not an error we want to catch, so reraise it # Not an error we want to catch, so reraise it

View file

@ -61,7 +61,10 @@ def setup_kodi_default_entries():
def reset_cached_images(): def reset_cached_images():
LOG.info('Resetting cached artwork') 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/') path = path_ops.translate_path('special://thumbnails/')
if path_ops.exists(path): if path_ops.exists(path):
path_ops.rmtree(path, ignore_errors=True) 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) new_path = path_ops.translate_path('special://thumbnails/%s' % path)
try: try:
path_ops.makedirs(path_ops.encode_path(new_path)) path_ops.makedirs(path_ops.encode_path(new_path))
except OSError: except OSError as err:
pass LOG.warn('Could not create thumbnail directory %s: %s',
with KodiTextureDB() as kodidb: new_path, err)
kodidb.reset_cached_images() LOG.info('Done resetting cached artwork')
def wipe_dbs(music=True): def wipe_dbs(music=True):
@ -83,16 +86,18 @@ def wipe_dbs(music=True):
Completely resets the Kodi databases 'video', 'texture' and 'music' (if Completely resets the Kodi databases 'video', 'texture' and 'music' (if
music sync is enabled) music sync is enabled)
DO NOT use context menu as we need to connect without WAL mode - if Kodi We need to connect without sqlite WAL mode as Kodi might still be accessing
is still accessing the DB the dbs and we need to prevent that
""" """
LOG.warn('Wiping Kodi databases!') 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: if music:
kinds.insert(1, KodiMusicDB) LOG.info('Wiping Kodi music database')
for kind in kinds: with KodiMusicDB(wal_mode=False) as kodidb:
with kind() as kodidb:
kodidb.wipe() kodidb.wipe()
reset_cached_images()
setup_kodi_default_entries() setup_kodi_default_entries()
# Delete SQLITE wal files # Delete SQLITE wal files
import xbmc import xbmc

View file

@ -14,9 +14,12 @@ class KodiDBBase(object):
""" """
Kodi database methods used for all types of items 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 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._texture_db = texture_db
self.lock = lock self.lock = lock
@ -24,13 +27,15 @@ class KodiDBBase(object):
self.cursor = self.kodiconn.cursor() if self.kodiconn else None self.cursor = self.kodiconn.cursor() if self.kodiconn else None
self.artconn = artconn self.artconn = artconn
self.artcursor = self.artconn.cursor() if self.artconn else None self.artcursor = self.artconn.cursor() if self.artconn else None
self.wal_mode = wal_mode
def __enter__(self): def __enter__(self):
if self.lock: if self.lock:
KODIDB_LOCK.acquire() 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.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 self.artcursor = self.artconn.cursor() if self._texture_db else None
return self return self

View file

@ -3,7 +3,6 @@
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
from . import common from . import common
from .. import db
class KodiTextureDB(common.KodiDBBase): class KodiTextureDB(common.KodiDBBase):
@ -16,11 +15,3 @@ class KodiTextureDB(common.KodiDBBase):
self.artcursor.execute('SELECT url FROM texture WHERE url = ? LIMIT 1', self.artcursor.execute('SELECT url FROM texture WHERE url = ? LIMIT 1',
(url, )) (url, ))
return self.artcursor.fetchone() is None 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])

View file

@ -573,12 +573,10 @@ def wipe_database(reboot=True):
# Plex DB completely empty yet. Wipe existing Kodi music only if we # Plex DB completely empty yet. Wipe existing Kodi music only if we
# expect to sync Plex music # expect to sync Plex music
music = settings('enableMusic') == 'true' music = settings('enableMusic') == 'true'
LOG.info("Resetting all cached artwork.")
kodi_db.wipe_dbs(music) kodi_db.wipe_dbs(music)
plex_db.wipe() plex_db.wipe()
LOG.info("Resetting all cached artwork.")
# Remove all cached artwork
kodi_db.reset_cached_images()
# reset the install run flag # reset the install run flag
settings('SyncInstallRunDone', value="false") settings('SyncInstallRunDone', value="false")
settings('sections_asked_for_machine_identifier', value='') settings('sections_asked_for_machine_identifier', value='')