PlexKodiConnect/resources/lib/plex_db/common.py

349 lines
13 KiB
Python
Raw Normal View History

2018-10-22 01:56:13 +11:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
from threading import Lock
2018-10-22 01:56:13 +11:00
from .. import db, variables as v
2018-10-22 01:56:13 +11:00
PLEXDB_LOCK = Lock()
2018-10-25 02:17:02 +11:00
SUPPORTED_KODI_TYPES = (
v.KODI_TYPE_MOVIE,
v.KODI_TYPE_SHOW,
v.KODI_TYPE_SEASON,
v.KODI_TYPE_EPISODE,
v.KODI_TYPE_ARTIST,
v.KODI_TYPE_ALBUM,
2018-11-04 02:59:59 +11:00
v.KODI_TYPE_SONG
)
2018-10-25 02:17:02 +11:00
2018-10-23 22:54:09 +11:00
class PlexDBBase(object):
2018-10-22 01:56:13 +11:00
"""
Plex database methods used for all types of items.
2018-10-22 01:56:13 +11:00
"""
def __init__(self, plexconn=None, lock=True, copy=False):
2018-10-23 22:54:09 +11:00
# Allows us to use this class with a cursor instead of context mgr
self.plexconn = plexconn
self.cursor = self.plexconn.cursor() if self.plexconn else None
self.lock = lock
self.copy = copy
2018-10-22 01:56:13 +11:00
2018-10-23 22:54:09 +11:00
def __enter__(self):
if self.lock:
PLEXDB_LOCK.acquire()
self.plexconn = db.connect('plex-copy' if self.copy else 'plex')
2018-10-23 22:54:09 +11:00
self.cursor = self.plexconn.cursor()
return self
def __exit__(self, e_typ, e_val, trcbak):
try:
if e_typ:
# re-raise any exception
return False
self.plexconn.commit()
finally:
self.plexconn.close()
if self.lock:
PLEXDB_LOCK.release()
2018-10-23 22:54:09 +11:00
2018-10-26 01:07:34 +11:00
def is_recorded(self, plex_id, plex_type):
"""
FAST method to check whether a plex_id has already been recorded
"""
2018-11-09 01:15:52 +11:00
self.cursor.execute('SELECT plex_id FROM %s WHERE plex_id = ?' % plex_type,
(plex_id, ))
2018-10-26 01:07:34 +11:00
return self.cursor.fetchone() is not None
2018-10-24 19:57:52 +11:00
def item_by_id(self, plex_id, plex_type=None):
"""
2018-10-25 02:17:02 +11:00
Returns the item for plex_id or None.
Supply with the correct plex_type to speed up lookup
2018-10-24 19:57:52 +11:00
"""
answ = None
if plex_type == v.PLEX_TYPE_MOVIE:
2018-11-04 02:56:51 +11:00
answ = self.movie(plex_id)
2018-10-24 19:57:52 +11:00
elif plex_type == v.PLEX_TYPE_EPISODE:
2018-11-04 02:56:51 +11:00
answ = self.episode(plex_id)
2018-10-24 19:57:52 +11:00
elif plex_type == v.PLEX_TYPE_SHOW:
2018-11-04 02:56:51 +11:00
answ = self.show(plex_id)
2018-10-24 19:57:52 +11:00
elif plex_type == v.PLEX_TYPE_SEASON:
2018-11-04 02:56:51 +11:00
answ = self.season(plex_id)
2018-11-04 02:59:46 +11:00
elif plex_type == v.PLEX_TYPE_SONG:
answ = self.song(plex_id)
elif plex_type == v.PLEX_TYPE_ALBUM:
answ = self.album(plex_id)
elif plex_type == v.PLEX_TYPE_ARTIST:
answ = self.artist(plex_id)
2019-01-09 04:00:54 +11:00
elif plex_type in (v.PLEX_TYPE_CLIP, v.PLEX_TYPE_PHOTO, v.PLEX_TYPE_PLAYLIST):
# Will never be synched to Kodi
pass
elif plex_type is None:
2018-10-24 19:57:52 +11:00
# SLOW - lookup plex_id in all our tables
for kind in (v.PLEX_TYPE_MOVIE,
v.PLEX_TYPE_EPISODE,
2018-11-04 02:59:46 +11:00
v.PLEX_TYPE_SHOW,
v.PLEX_TYPE_SEASON,
2018-11-05 03:00:34 +11:00
'song', # darn
2018-11-04 02:59:46 +11:00
v.PLEX_TYPE_ALBUM,
v.PLEX_TYPE_ARTIST):
2018-10-24 19:57:52 +11:00
method = getattr(self, kind)
2018-11-04 02:56:51 +11:00
answ = method(plex_id)
if answ:
2018-10-24 19:57:52 +11:00
break
return answ
2018-10-25 02:17:02 +11:00
def item_by_kodi_id(self, kodi_id, kodi_type):
"""
"""
if kodi_type not in SUPPORTED_KODI_TYPES:
return
2018-11-09 01:15:52 +11:00
self.cursor.execute('SELECT * from %s WHERE kodi_id = ? LIMIT 1'
% v.PLEX_TYPE_FROM_KODI_TYPE[kodi_type],
(kodi_id, ))
2018-10-25 02:17:02 +11:00
method = getattr(self, 'entry_to_%s' % v.PLEX_TYPE_FROM_KODI_TYPE[kodi_type])
return method(self.cursor.fetchone())
def plex_id_by_last_sync(self, plex_type, last_sync, limit):
2018-10-23 22:54:09 +11:00
"""
Returns an iterator for all items where the last_sync is NOT identical
2018-10-22 01:56:13 +11:00
"""
query = '''
SELECT plex_id FROM %s WHERE last_sync <> ? LIMIT %s
''' % (plex_type, limit)
return (x[0] for x in self.cursor.execute(query, (last_sync, )))
2018-10-22 01:56:13 +11:00
def checksum(self, plex_id, plex_type):
2018-10-29 03:02:06 +11:00
"""
Returns the checksum for plex_id
2018-10-29 03:02:06 +11:00
"""
2018-11-09 01:15:52 +11:00
self.cursor.execute('SELECT checksum FROM %s WHERE plex_id = ? LIMIT 1' % plex_type,
(plex_id, ))
2018-10-29 03:02:06 +11:00
try:
return self.cursor.fetchone()[0]
except TypeError:
pass
def update_last_sync(self, plex_id, plex_type, last_sync):
2018-10-22 01:56:13 +11:00
"""
2018-10-23 22:54:09 +11:00
Sets a new timestamp for plex_id
"""
2018-11-09 01:15:52 +11:00
self.cursor.execute('UPDATE %s SET last_sync = ? WHERE plex_id = ?' % plex_type,
(last_sync, plex_id))
2018-10-23 22:54:09 +11:00
def remove(self, plex_id, plex_type):
"""
Removes the item from our Plex db
"""
2018-11-25 00:06:21 +11:00
self.cursor.execute('DELETE FROM %s WHERE plex_id = ?' % plex_type, (plex_id, ))
2018-10-23 22:54:09 +11:00
2018-12-26 05:12:49 +11:00
def every_plex_id(self, plex_type, offset, limit):
2018-11-03 20:36:37 +11:00
"""
Returns an iterator for plex_type for every single plex_id
2018-12-26 05:12:49 +11:00
Will start with records at DB position offset [int] and return limit
[int] number of items
2018-11-03 20:36:37 +11:00
"""
2018-11-09 01:15:52 +11:00
return (x[0] for x in
2018-12-26 05:12:49 +11:00
self.cursor.execute('SELECT plex_id FROM %s LIMIT %s OFFSET %s'
% (plex_type, limit, offset)))
2018-11-03 20:36:37 +11:00
2018-12-26 05:12:49 +11:00
def missing_fanart(self, plex_type, offset, limit):
2018-10-24 19:57:52 +11:00
"""
Returns an iterator for plex_type for all plex_id, where fanart_synced
has not yet been set to 1
2018-12-26 05:12:49 +11:00
Will start with records at DB position offset [int] and return limit
[int] number of items
"""
query = '''
SELECT plex_id FROM %s WHERE fanart_synced = 0
LIMIT %s OFFSET %s
''' % (plex_type, limit, offset)
return (x[0] for x in self.cursor.execute(query))
2018-10-24 19:57:52 +11:00
def missing_trailers(self, plex_type, offset, limit):
"""
Returns an iterator for plex_type for all plex_id, where trailer_synced
has not yet been set to 1
Will start with records at DB position offset [int] and return limit
[int] number of items
"""
query = '''
SELECT plex_id FROM %s WHERE trailer_synced = 0
LIMIT %s OFFSET %s
''' % (plex_type, limit, offset)
return (x[0] for x in self.cursor.execute(query))
2018-10-24 19:57:52 +11:00
def set_fanart_synced(self, plex_id, plex_type):
"""
Toggles fanart_synced to 1 for plex_id
"""
2018-11-09 01:15:52 +11:00
self.cursor.execute('UPDATE %s SET fanart_synced = 1 WHERE plex_id = ?' % plex_type,
(plex_id, ))
2018-10-24 19:57:52 +11:00
def set_trailer_synced(self, plex_id, plex_type):
"""
Toggles fanart_synced to 1 for plex_id
"""
self.cursor.execute('UPDATE %s SET trailer_synced = 1 WHERE plex_id = ?' % plex_type,
(plex_id, ))
def plexid_by_sectionid(self, section_id, plex_type, limit):
query = '''
SELECT plex_id FROM %s WHERE section_id = ? LIMIT %s
''' % (plex_type, limit)
return (x[0] for x in self.cursor.execute(query, (section_id, )))
2018-11-26 06:15:38 +11:00
2018-11-27 03:32:21 +11:00
def kodiid_by_sectionid(self, section_id, plex_type):
return (x[0] for x in
self.cursor.execute('SELECT kodi_id FROM %s WHERE section_id = ?' % plex_type,
(section_id, )))
2018-10-23 22:54:09 +11:00
def initialize():
"""
2018-10-24 19:57:52 +11:00
Run once upon PKC startup to verify that plex db exists.
2018-10-23 22:54:09 +11:00
"""
2018-10-24 19:57:52 +11:00
with PlexDBBase() as plexdb:
plexdb.cursor.execute('''
CREATE TABLE IF NOT EXISTS version(
idVersion TEXT PRIMARY KEY)
''')
plexdb.cursor.execute('''
INSERT OR REPLACE INTO version(idVersion)
VALUES (?)
''', (v.ADDON_VERSION, ))
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS sections(
section_id INTEGER PRIMARY KEY,
section_name TEXT,
plex_type TEXT,
kodi_tagid INTEGER,
sync_to_kodi INTEGER,
last_sync INTEGER)
2018-10-23 22:54:09 +11:00
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS movie(
2018-10-24 19:57:52 +11:00
plex_id INTEGER PRIMARY KEY,
2018-10-23 22:54:09 +11:00
checksum INTEGER UNIQUE,
section_id INTEGER,
kodi_id INTEGER,
kodi_fileid INTEGER,
kodi_pathid INTEGER,
fanart_synced INTEGER,
trailer_synced BOOLEAN,
2018-10-23 22:54:09 +11:00
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS show(
2018-10-24 19:57:52 +11:00
plex_id INTEGER PRIMARY KEY,
2018-10-23 22:54:09 +11:00
checksum INTEGER UNIQUE,
section_id INTEGER,
kodi_id INTEGER,
kodi_pathid INTEGER,
fanart_synced INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS season(
plex_id INTEGER PRIMARY KEY,
checksum INTEGER UNIQUE,
section_id INTEGER,
2018-10-25 03:07:51 +11:00
show_id INTEGER,
parent_id INTEGER,
2018-10-23 22:54:09 +11:00
kodi_id INTEGER,
fanart_synced INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS episode(
plex_id INTEGER PRIMARY KEY,
checksum INTEGER UNIQUE,
section_id INTEGER,
2018-10-25 03:07:51 +11:00
show_id INTEGER,
grandparent_id INTEGER,
season_id INTEGER,
parent_id INTEGER,
2018-10-23 22:54:09 +11:00
kodi_id INTEGER,
kodi_fileid INTEGER,
kodi_fileid_2 INTEGER,
2018-10-23 22:54:09 +11:00
kodi_pathid INTEGER,
fanart_synced INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS artist(
2018-10-24 19:57:52 +11:00
plex_id INTEGER PRIMARY KEY,
2018-10-23 22:54:09 +11:00
checksum INTEGER UNIQUE,
section_id INTEGER,
kodi_id INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS album(
plex_id INTEGER PRIMARY KEY,
checksum INTEGER UNIQUE,
section_id INTEGER,
2018-10-25 03:07:51 +11:00
artist_id INTEGER,
parent_id INTEGER,
2018-10-23 22:54:09 +11:00
kodi_id INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS track(
plex_id INTEGER PRIMARY KEY,
checksum INTEGER UNIQUE,
section_id INTEGER,
2018-10-25 03:07:51 +11:00
artist_id INTEGER,
grandparent_id INTEGER,
album_id INTEGER,
parent_id INTEGER,
2018-10-23 22:54:09 +11:00
kodi_id INTEGER,
kodi_pathid INTEGER,
last_sync INTEGER)
''')
2018-10-24 19:57:52 +11:00
plexdb.cursor.execute('''
2018-10-23 22:54:09 +11:00
CREATE TABLE IF NOT EXISTS playlists(
2018-10-24 19:57:52 +11:00
plex_id INTEGER PRIMARY KEY,
2018-10-23 22:54:09 +11:00
plex_name TEXT,
plex_updatedat INTEGER,
kodi_path TEXT,
kodi_type TEXT,
kodi_hash TEXT)
''')
2018-11-12 05:22:32 +11:00
# DB indicees for faster lookups
2018-11-12 06:11:19 +11:00
commands = (
'CREATE INDEX IF NOT EXISTS ix_movie_1 ON movie (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_movie_2 ON movie (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_show_1 ON show (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_show_2 ON show (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_season_1 ON season (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_season_2 ON season (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_episode_1 ON episode (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_episode_2 ON episode (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_artist_1 ON artist (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_artist_2 ON artist (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_album_1 ON album (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_album_2 ON album (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_track_1 ON track (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_track_2 ON track (kodi_id)',
2018-11-12 06:11:19 +11:00
'CREATE UNIQUE INDEX IF NOT EXISTS ix_playlists_2 ON playlists (kodi_path)',
'CREATE INDEX IF NOT EXISTS ix_playlists_3 ON playlists (kodi_hash)',
2018-11-12 06:11:19 +11:00
)
for cmd in commands:
plexdb.cursor.execute(cmd)
2018-10-23 22:54:09 +11:00
def wipe(table=None):
2018-10-23 22:54:09 +11:00
"""
Completely resets the Plex database.
If a table [unicode] name is provided, only that table will be dropped
2018-10-23 22:54:09 +11:00
"""
2018-10-24 19:57:52 +11:00
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()]
2018-10-23 22:54:09 +11:00
for table in tables:
2018-11-09 01:15:52 +11:00
plexdb.cursor.execute('DROP table IF EXISTS %s' % table)