Rewire kodi database access
This commit is contained in:
parent
150229061b
commit
a16eae143a
22 changed files with 1078 additions and 934 deletions
|
@ -6,10 +6,10 @@ from urllib import quote_plus, unquote
|
||||||
import requests
|
import requests
|
||||||
import xbmc
|
import xbmc
|
||||||
|
|
||||||
from . import backgroundthread, path_ops, utils
|
from .kodi_db import KodiVideoDB, KodiMusicDB, KodiTextureDB
|
||||||
|
from . import backgroundthread, utils
|
||||||
from . import state
|
from . import state
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
LOG = getLogger('PLEX.artwork')
|
LOG = getLogger('PLEX.artwork')
|
||||||
|
|
||||||
# Disable annoying requests warnings
|
# Disable annoying requests warnings
|
||||||
|
@ -26,8 +26,6 @@ IMAGE_CACHING_SUSPENDS = [
|
||||||
if not utils.settings('imageSyncDuringPlayback') == 'true':
|
if not utils.settings('imageSyncDuringPlayback') == 'true':
|
||||||
IMAGE_CACHING_SUSPENDS.append(state.SUSPEND_SYNC)
|
IMAGE_CACHING_SUSPENDS.append(state.SUSPEND_SYNC)
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
def double_urlencode(text):
|
def double_urlencode(text):
|
||||||
return quote_plus(quote_plus(text))
|
return quote_plus(quote_plus(text))
|
||||||
|
@ -53,18 +51,16 @@ class ImageCachingThread(backgroundthread.KillableThread):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _art_url_generator():
|
def _art_url_generator():
|
||||||
from . import kodidb_functions as kodidb
|
for kind in (KodiVideoDB, KodiMusicDB):
|
||||||
for kind in ('video', 'music'):
|
with kind() as kodidb:
|
||||||
with kodidb.GetKodiDB(kind) as kodi_db:
|
|
||||||
for kodi_type in ('poster', 'fanart'):
|
for kodi_type in ('poster', 'fanart'):
|
||||||
for url in kodi_db.artwork_generator(kodi_type):
|
for url in kodidb.artwork_generator(kodi_type):
|
||||||
yield url
|
yield url
|
||||||
|
|
||||||
def missing_art_cache_generator(self):
|
def missing_art_cache_generator(self):
|
||||||
from . import kodidb_functions as kodidb
|
with KodiTextureDB() as kodidb:
|
||||||
with kodidb.GetKodiDB('texture') as kodi_db:
|
|
||||||
for url in self._art_url_generator():
|
for url in self._art_url_generator():
|
||||||
if kodi_db.url_not_yet_cached(url):
|
if kodidb.url_not_yet_cached(url):
|
||||||
yield url
|
yield url
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -126,74 +122,3 @@ def cache_url(url):
|
||||||
break
|
break
|
||||||
# We did not even get a timeout
|
# We did not even get a timeout
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def modify_artwork(artworks, kodi_id, kodi_type, cursor):
|
|
||||||
"""
|
|
||||||
Pass in an artworks dict (see PlexAPI) to set an items artwork.
|
|
||||||
"""
|
|
||||||
for kodi_art, url in artworks.iteritems():
|
|
||||||
modify_art(url, kodi_id, kodi_type, kodi_art, cursor)
|
|
||||||
|
|
||||||
|
|
||||||
def modify_art(url, kodi_id, kodi_type, kodi_art, cursor):
|
|
||||||
"""
|
|
||||||
Adds or modifies the artwork of kind kodi_art (e.g. 'poster') in the
|
|
||||||
Kodi art table for item kodi_id/kodi_type. Will also cache everything
|
|
||||||
except actor portraits.
|
|
||||||
"""
|
|
||||||
query = '''
|
|
||||||
SELECT url FROM art
|
|
||||||
WHERE media_id = ? AND media_type = ? AND type = ?
|
|
||||||
LIMIT 1
|
|
||||||
'''
|
|
||||||
cursor.execute(query, (kodi_id, kodi_type, kodi_art,))
|
|
||||||
try:
|
|
||||||
# Update the artwork
|
|
||||||
old_url = cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Add the artwork
|
|
||||||
query = '''
|
|
||||||
INSERT INTO art(media_id, media_type, type, url)
|
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
'''
|
|
||||||
cursor.execute(query, (kodi_id, kodi_type, kodi_art, url))
|
|
||||||
else:
|
|
||||||
if url == old_url:
|
|
||||||
# Only cache artwork if it changed
|
|
||||||
return
|
|
||||||
delete_cached_artwork(old_url)
|
|
||||||
query = '''
|
|
||||||
UPDATE art SET url = ?
|
|
||||||
WHERE media_id = ? AND media_type = ? AND type = ?
|
|
||||||
'''
|
|
||||||
cursor.execute(query, (url, kodi_id, kodi_type, kodi_art))
|
|
||||||
|
|
||||||
|
|
||||||
def delete_artwork(kodiId, mediaType, cursor):
|
|
||||||
cursor.execute('SELECT url FROM art WHERE media_id = ? AND media_type = ?',
|
|
||||||
(kodiId, mediaType, ))
|
|
||||||
for row in cursor.fetchall():
|
|
||||||
delete_cached_artwork(row[0])
|
|
||||||
|
|
||||||
|
|
||||||
def delete_cached_artwork(url):
|
|
||||||
"""
|
|
||||||
Deleted the cached artwork with path url (if it exists)
|
|
||||||
"""
|
|
||||||
from . import kodidb_functions as kodidb
|
|
||||||
with kodidb.GetKodiDB('texture') as kodi_db:
|
|
||||||
try:
|
|
||||||
kodi_db.cursor.execute("SELECT cachedurl FROM texture WHERE url=? LIMIT 1",
|
|
||||||
(url, ))
|
|
||||||
cachedurl = kodi_db.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Could not find cached url
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# Delete thumbnail as well as the entry
|
|
||||||
path = path_ops.translate_path("special://thumbnails/%s"
|
|
||||||
% cachedurl)
|
|
||||||
if path_ops.exists(path):
|
|
||||||
path_ops.rmtree(path, ignore_errors=True)
|
|
||||||
kodi_db.cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ from ntpath import dirname
|
||||||
|
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import kodidb_functions as kodidb
|
from ..kodi_db import KodiVideoDB
|
||||||
from .. import artwork, utils
|
from .. import utils
|
||||||
|
|
||||||
LOG = getLogger('PLEX.itemtypes.common')
|
LOG = getLogger('PLEX.itemtypes.common')
|
||||||
|
|
||||||
|
@ -39,14 +39,16 @@ class ItemBase(object):
|
||||||
Input:
|
Input:
|
||||||
kodiType: optional argument; e.g. 'video' or 'music'
|
kodiType: optional argument; e.g. 'video' or 'music'
|
||||||
"""
|
"""
|
||||||
def __init__(self, last_sync, plexdb=None, kodi_db=None):
|
def __init__(self, last_sync, plexdb=None, kodidb=None):
|
||||||
self.last_sync = last_sync
|
self.last_sync = last_sync
|
||||||
self.plexconn = None
|
self.plexconn = None
|
||||||
self.plexcursor = plexdb.cursor if plexdb else None
|
self.plexcursor = plexdb.cursor if plexdb else None
|
||||||
self.kodiconn = None
|
self.kodiconn = None
|
||||||
self.kodicursor = kodi_db.cursor if kodi_db else None
|
self.kodicursor = kodidb.cursor if kodidb else None
|
||||||
|
self.artconn = kodidb.artconn if kodidb else None
|
||||||
|
self.artcursor = kodidb.artcursor if kodidb else None
|
||||||
self.plexdb = plexdb
|
self.plexdb = plexdb
|
||||||
self.kodi_db = kodi_db
|
self.kodidb = kodidb
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -56,8 +58,12 @@ class ItemBase(object):
|
||||||
self.plexcursor = self.plexconn.cursor()
|
self.plexcursor = self.plexconn.cursor()
|
||||||
self.kodiconn = utils.kodi_sql('video')
|
self.kodiconn = utils.kodi_sql('video')
|
||||||
self.kodicursor = self.kodiconn.cursor()
|
self.kodicursor = self.kodiconn.cursor()
|
||||||
|
self.artconn = utils.kodi_sql('texture')
|
||||||
|
self.artcursor = self.artconn.cursor()
|
||||||
self.plexdb = PlexDB(self.plexcursor)
|
self.plexdb = PlexDB(self.plexcursor)
|
||||||
self.kodi_db = kodidb.KodiDBMethods(self.kodicursor)
|
self.kodidb = KodiVideoDB(texture_db=True,
|
||||||
|
cursor=self.kodicursor,
|
||||||
|
artcursor=self.artcursor)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
@ -66,18 +72,19 @@ class ItemBase(object):
|
||||||
"""
|
"""
|
||||||
self.plexconn.commit()
|
self.plexconn.commit()
|
||||||
self.kodiconn.commit()
|
self.kodiconn.commit()
|
||||||
|
self.artconn.commit()
|
||||||
self.plexconn.close()
|
self.plexconn.close()
|
||||||
self.kodiconn.close()
|
self.kodiconn.close()
|
||||||
|
self.artconn.close()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_fanart(self, artworks, kodi_id, kodi_type):
|
def set_fanart(self, artworks, kodi_id, kodi_type):
|
||||||
"""
|
"""
|
||||||
Writes artworks [dict containing only set artworks] to the Kodi art DB
|
Writes artworks [dict containing only set artworks] to the Kodi art DB
|
||||||
"""
|
"""
|
||||||
artwork.modify_artwork(artworks,
|
self.kodidb.modify_artwork(artworks,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
kodi_type,
|
kodi_type)
|
||||||
self.kodicursor)
|
|
||||||
|
|
||||||
def update_userdata(self, xml_element, plex_type):
|
def update_userdata(self, xml_element, plex_type):
|
||||||
"""
|
"""
|
||||||
|
@ -93,13 +100,13 @@ class ItemBase(object):
|
||||||
# Grab the user's viewcount, resume points etc. from PMS' answer
|
# Grab the user's viewcount, resume points etc. from PMS' answer
|
||||||
userdata = api.userdata()
|
userdata = api.userdata()
|
||||||
# Write to Kodi DB
|
# Write to Kodi DB
|
||||||
self.kodi_db.set_resume(db_item['kodi_fileid'],
|
self.kodidb.set_resume(db_item['kodi_fileid'],
|
||||||
userdata['Resume'],
|
userdata['Resume'],
|
||||||
userdata['Runtime'],
|
userdata['Runtime'],
|
||||||
userdata['PlayCount'],
|
userdata['PlayCount'],
|
||||||
userdata['LastPlayedDate'],
|
userdata['LastPlayedDate'],
|
||||||
plex_type)
|
plex_type)
|
||||||
self.kodi_db.update_userrating(db_item['kodi_id'],
|
self.kodidb.update_userrating(db_item['kodi_id'],
|
||||||
db_item['kodi_type'],
|
db_item['kodi_type'],
|
||||||
userdata['UserRating'])
|
userdata['UserRating'])
|
||||||
|
|
||||||
|
@ -118,7 +125,7 @@ class ItemBase(object):
|
||||||
view_count = 1
|
view_count = 1
|
||||||
resume = 0
|
resume = 0
|
||||||
# Do the actual update
|
# Do the actual update
|
||||||
self.kodi_db.set_resume(kodi_fileid,
|
self.kodidb.set_resume(kodi_fileid,
|
||||||
resume,
|
resume,
|
||||||
duration,
|
duration,
|
||||||
view_count,
|
view_count,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from logging import getLogger
|
||||||
|
|
||||||
from .common import ItemBase
|
from .common import ItemBase
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from .. import artwork, state, variables as v, plex_functions as PF
|
from .. import state, variables as v, plex_functions as PF
|
||||||
|
|
||||||
LOG = getLogger('PLEX.movies')
|
LOG = getLogger('PLEX.movies')
|
||||||
|
|
||||||
|
@ -20,32 +20,20 @@ class Movie(ItemBase):
|
||||||
Process single movie
|
Process single movie
|
||||||
"""
|
"""
|
||||||
api = API(xml)
|
api = API(xml)
|
||||||
update_item = True
|
|
||||||
plex_id = api.plex_id()
|
plex_id = api.plex_id()
|
||||||
# Cannot parse XML, abort
|
# Cannot parse XML, abort
|
||||||
if not plex_id:
|
if not plex_id:
|
||||||
LOG.error('Cannot parse XML data for movie: %s', xml.attrib)
|
LOG.error('Cannot parse XML data for movie: %s', xml.attrib)
|
||||||
return
|
return
|
||||||
movie = self.plexdb.movie(plex_id)
|
movie = self.plexdb.movie(plex_id)
|
||||||
try:
|
if movie:
|
||||||
|
update_item = True
|
||||||
kodi_id = movie['kodi_id']
|
kodi_id = movie['kodi_id']
|
||||||
old_kodi_fileid = movie['kodi_fileid']
|
old_kodi_fileid = movie['kodi_fileid']
|
||||||
kodi_pathid = movie['kodi_pathid']
|
kodi_pathid = movie['kodi_pathid']
|
||||||
except TypeError:
|
|
||||||
update_item = False
|
|
||||||
self.kodicursor.execute('SELECT COALESCE(MAX(idMovie), 0) FROM movie')
|
|
||||||
kodi_id = self.kodicursor.fetchone()[0] + 1
|
|
||||||
else:
|
else:
|
||||||
# Verification the item is still in Kodi
|
|
||||||
self.kodicursor.execute('SELECT idMovie FROM movie WHERE idMovie = ? LIMIT 1',
|
|
||||||
(kodi_id, ))
|
|
||||||
try:
|
|
||||||
self.kodicursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# item is not found, let's recreate it.
|
|
||||||
update_item = False
|
update_item = False
|
||||||
LOG.info("kodi_id: %s missing from Kodi, repairing the entry.",
|
kodi_id = self.kodidb.new_movie_id()
|
||||||
kodi_id)
|
|
||||||
|
|
||||||
userdata = api.userdata()
|
userdata = api.userdata()
|
||||||
playcount = userdata['PlayCount']
|
playcount = userdata['PlayCount']
|
||||||
|
@ -80,7 +68,7 @@ class Movie(ItemBase):
|
||||||
# Network share
|
# Network share
|
||||||
filename = playurl.rsplit("/", 1)[1]
|
filename = playurl.rsplit("/", 1)[1]
|
||||||
path = playurl.replace(filename, "")
|
path = playurl.replace(filename, "")
|
||||||
kodi_pathid = self.kodi_db.add_video_path(path,
|
kodi_pathid = self.kodidb.add_video_path(path,
|
||||||
content='movies',
|
content='movies',
|
||||||
scraper='metadata.local')
|
scraper='metadata.local')
|
||||||
if do_indirect:
|
if do_indirect:
|
||||||
|
@ -90,19 +78,19 @@ class Movie(ItemBase):
|
||||||
filename = ('%s?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
filename = ('%s?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
% (path, plex_id, v.PLEX_TYPE_MOVIE, filename))
|
% (path, plex_id, v.PLEX_TYPE_MOVIE, filename))
|
||||||
playurl = filename
|
playurl = filename
|
||||||
kodi_pathid = self.kodi_db.get_path(path)
|
kodi_pathid = self.kodidb.get_path(path)
|
||||||
|
|
||||||
file_id = self.kodi_db.add_file(filename,
|
file_id = self.kodidb.add_file(filename,
|
||||||
kodi_pathid,
|
kodi_pathid,
|
||||||
api.date_created())
|
api.date_created())
|
||||||
|
|
||||||
if update_item:
|
if update_item:
|
||||||
LOG.info('UPDATE movie plex_id: %s - %s', plex_id, api.title())
|
LOG.info('UPDATE movie plex_id: %s - %s', plex_id, api.title())
|
||||||
if file_id != old_kodi_fileid:
|
if file_id != old_kodi_fileid:
|
||||||
self.kodi_db.remove_file(old_kodi_fileid)
|
self.kodidb.remove_file(old_kodi_fileid)
|
||||||
rating_id = self.kodi_db.get_ratingid(kodi_id,
|
rating_id = self.kodidb.get_ratingid(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE)
|
v.KODI_TYPE_MOVIE)
|
||||||
self.kodi_db.update_ratings(kodi_id,
|
self.kodidb.update_ratings(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE,
|
||||||
"default",
|
"default",
|
||||||
rating,
|
rating,
|
||||||
|
@ -110,30 +98,30 @@ class Movie(ItemBase):
|
||||||
rating_id)
|
rating_id)
|
||||||
# update new uniqueid Kodi 17
|
# update new uniqueid Kodi 17
|
||||||
if api.provider('imdb') is not None:
|
if api.provider('imdb') is not None:
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE)
|
v.KODI_TYPE_MOVIE)
|
||||||
self.kodi_db.update_uniqueid(kodi_id,
|
self.kodidb.update_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE,
|
||||||
api.provider('imdb'),
|
api.provider('imdb'),
|
||||||
"imdb",
|
"imdb",
|
||||||
uniqueid)
|
uniqueid)
|
||||||
else:
|
else:
|
||||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_MOVIE)
|
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_MOVIE)
|
||||||
uniqueid = -1
|
uniqueid = -1
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD movie plex_id: %s - %s", plex_id, title)
|
LOG.info("ADD movie plex_id: %s - %s", plex_id, title)
|
||||||
rating_id = self.kodi_db.get_ratingid(kodi_id,
|
rating_id = self.kodidb.get_ratingid(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE)
|
v.KODI_TYPE_MOVIE)
|
||||||
self.kodi_db.add_ratings(rating_id,
|
self.kodidb.add_ratings(rating_id,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE,
|
||||||
"default",
|
"default",
|
||||||
rating,
|
rating,
|
||||||
api.votecount())
|
api.votecount())
|
||||||
if api.provider('imdb') is not None:
|
if api.provider('imdb') is not None:
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE)
|
v.KODI_TYPE_MOVIE)
|
||||||
self.kodi_db.add_uniqueid(uniqueid,
|
self.kodidb.add_uniqueid(uniqueid,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE,
|
||||||
api.provider('imdb'),
|
api.provider('imdb'),
|
||||||
|
@ -142,42 +130,48 @@ class Movie(ItemBase):
|
||||||
uniqueid = -1
|
uniqueid = -1
|
||||||
|
|
||||||
# Update Kodi's main entry
|
# Update Kodi's main entry
|
||||||
query = '''
|
self.kodidb.add_movie(kodi_id,
|
||||||
INSERT OR REPLACE INTO movie(idMovie, idFile, c00, c01, c02, c03,
|
file_id,
|
||||||
c04, c05, c06, c07, c09, c10, c11, c12, c14, c15, c16,
|
title,
|
||||||
c18, c19, c21, c22, c23, premiered, userrating)
|
api.plot(),
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
api.shortplot(),
|
||||||
?, ?, ?, ?, ?, ?, ?)
|
api.tagline(),
|
||||||
'''
|
api.votecount(),
|
||||||
self.kodicursor.execute(
|
rating_id,
|
||||||
query,
|
api.list_to_string(people['Writer']),
|
||||||
(kodi_id, file_id, title, api.plot(), api.shortplot(),
|
api.year(),
|
||||||
api.tagline(), api.votecount(), rating_id,
|
uniqueid,
|
||||||
api.list_to_string(people['Writer']), api.year(),
|
api.sorttitle(),
|
||||||
uniqueid, api.sorttitle(), runtime, api.content_rating(),
|
runtime,
|
||||||
api.list_to_string(genres), api.list_to_string(people['Director']),
|
api.content_rating(),
|
||||||
title, api.list_to_string(studios), api.trailer(),
|
api.list_to_string(genres),
|
||||||
api.list_to_string(countries), playurl, kodi_pathid,
|
api.list_to_string(people['Director']),
|
||||||
api.premiere_date(), userdata['UserRating']))
|
title,
|
||||||
|
api.list_to_string(studios),
|
||||||
|
api.trailer(),
|
||||||
|
api.list_to_string(countries),
|
||||||
|
playurl,
|
||||||
|
kodi_pathid,
|
||||||
|
api.premiere_date(),
|
||||||
|
userdata['UserRating'])
|
||||||
|
|
||||||
self.kodi_db.modify_countries(kodi_id, v.KODI_TYPE_MOVIE, countries)
|
self.kodidb.modify_countries(kodi_id, v.KODI_TYPE_MOVIE, countries)
|
||||||
self.kodi_db.modify_people(kodi_id,
|
self.kodidb.modify_people(kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE,
|
||||||
api.people_list())
|
api.people_list())
|
||||||
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_MOVIE, genres)
|
self.kodidb.modify_genres(kodi_id, v.KODI_TYPE_MOVIE, genres)
|
||||||
artwork.modify_artwork(api.artwork(),
|
self.kodidb.modify_artwork(api.artwork(),
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_MOVIE,
|
v.KODI_TYPE_MOVIE)
|
||||||
self.kodicursor)
|
self.kodidb.modify_streams(file_id, api.mediastreams(), runtime)
|
||||||
self.kodi_db.modify_streams(file_id, api.mediastreams(), runtime)
|
self.kodidb.modify_studios(kodi_id, v.KODI_TYPE_MOVIE, studios)
|
||||||
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_MOVIE, studios)
|
|
||||||
tags = [section_name]
|
tags = [section_name]
|
||||||
if collections:
|
if collections:
|
||||||
for plex_set_id, set_name in collections:
|
for plex_set_id, set_name in collections:
|
||||||
tags.append(set_name)
|
tags.append(set_name)
|
||||||
# Add any sets from Plex collection tags
|
# Add any sets from Plex collection tags
|
||||||
kodi_set_id = self.kodi_db.create_collection(set_name)
|
kodi_set_id = self.kodidb.create_collection(set_name)
|
||||||
self.kodi_db.assign_collection(kodi_set_id, kodi_id)
|
self.kodidb.assign_collection(kodi_set_id, kodi_id)
|
||||||
if children is None:
|
if children is None:
|
||||||
# e.g. when added via websocket
|
# e.g. when added via websocket
|
||||||
LOG.debug('Costly looking up Plex collection %s: %s',
|
LOG.debug('Costly looking up Plex collection %s: %s',
|
||||||
|
@ -199,13 +193,12 @@ class Movie(ItemBase):
|
||||||
set_api = API(children[plex_set_id][0])
|
set_api = API(children[plex_set_id][0])
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
artwork.modify_artwork(set_api.artwork(),
|
self.kodidb.modify_artwork(set_api.artwork(),
|
||||||
kodi_set_id,
|
kodi_set_id,
|
||||||
v.KODI_TYPE_SET,
|
v.KODI_TYPE_SET)
|
||||||
self.kodicursor)
|
self.kodidb.modify_tags(kodi_id, v.KODI_TYPE_MOVIE, tags)
|
||||||
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_MOVIE, tags)
|
|
||||||
# Process playstate
|
# Process playstate
|
||||||
self.kodi_db.set_resume(file_id,
|
self.kodidb.set_resume(file_id,
|
||||||
resume,
|
resume,
|
||||||
runtime,
|
runtime,
|
||||||
playcount,
|
playcount,
|
||||||
|
@ -238,19 +231,18 @@ class Movie(ItemBase):
|
||||||
# Remove the plex reference
|
# Remove the plex reference
|
||||||
self.plexdb.remove(plex_id, v.PLEX_TYPE_MOVIE)
|
self.plexdb.remove(plex_id, v.PLEX_TYPE_MOVIE)
|
||||||
# Remove artwork
|
# Remove artwork
|
||||||
artwork.delete_artwork(kodi_id, kodi_type, self.self.kodicursor)
|
self.kodidb.delete_artwork(kodi_id, kodi_type)
|
||||||
set_id = self.kodi_db.get_set_id(kodi_id)
|
set_id = self.kodidb.get_set_id(kodi_id)
|
||||||
self.kodi_db.modify_countries(kodi_id, kodi_type)
|
self.kodidb.modify_countries(kodi_id, kodi_type)
|
||||||
self.kodi_db.modify_people(kodi_id, kodi_type)
|
self.kodidb.modify_people(kodi_id, kodi_type)
|
||||||
self.kodi_db.modify_genres(kodi_id, kodi_type)
|
self.kodidb.modify_genres(kodi_id, kodi_type)
|
||||||
self.kodi_db.modify_studios(kodi_id, kodi_type)
|
self.kodidb.modify_studios(kodi_id, kodi_type)
|
||||||
self.kodi_db.modify_tags(kodi_id, kodi_type)
|
self.kodidb.modify_tags(kodi_id, kodi_type)
|
||||||
# Delete kodi movie and file
|
# Delete kodi movie and file
|
||||||
self.kodi_db.remove_file(file_id)
|
self.kodidb.remove_file(file_id)
|
||||||
self.self.kodicursor.execute('DELETE FROM movie WHERE idMovie = ?',
|
self.kodidb.remove_movie(kodi_id)
|
||||||
(kodi_id,))
|
|
||||||
if set_id:
|
if set_id:
|
||||||
self.kodi_db.delete_possibly_empty_set(set_id)
|
self.kodidb.delete_possibly_empty_set(set_id)
|
||||||
self.kodi_db.remove_uniqueid(kodi_id, kodi_type)
|
self.kodidb.remove_uniqueid(kodi_id, kodi_type)
|
||||||
self.kodi_db.remove_ratings(kodi_id, kodi_type)
|
self.kodidb.remove_ratings(kodi_id, kodi_type)
|
||||||
LOG.debug('Deleted movie %s from kodi database', plex_id)
|
LOG.debug('Deleted movie %s from kodi database', plex_id)
|
||||||
|
|
|
@ -6,8 +6,8 @@ from logging import getLogger
|
||||||
from .common import ItemBase
|
from .common import ItemBase
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import artwork, kodidb_functions as kodidb
|
from ..kodi_db import KodiMusicDB
|
||||||
from .. import plex_functions as PF, utils, state, variables as v
|
from .. import artwork, plex_functions as PF, utils, state, variables as v
|
||||||
|
|
||||||
LOG = getLogger('PLEX.music')
|
LOG = getLogger('PLEX.music')
|
||||||
|
|
||||||
|
@ -21,8 +21,12 @@ class MusicMixin(object):
|
||||||
self.plexcursor = self.plexconn.cursor()
|
self.plexcursor = self.plexconn.cursor()
|
||||||
self.kodiconn = utils.kodi_sql('music')
|
self.kodiconn = utils.kodi_sql('music')
|
||||||
self.kodicursor = self.kodiconn.cursor()
|
self.kodicursor = self.kodiconn.cursor()
|
||||||
|
self.artconn = utils.kodi_sql('texture')
|
||||||
|
self.artcursor = self.artconn.cursor()
|
||||||
self.plexdb = PlexDB(self.plexcursor)
|
self.plexdb = PlexDB(self.plexcursor)
|
||||||
self.kodi_db = kodidb.KodiDBMethods(self.kodicursor)
|
self.kodidb = KodiMusicDB(texture_db=True,
|
||||||
|
cursor=self.kodicursor,
|
||||||
|
artcursor=self.artcursor)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def remove(self, plex_id, plex_type=None):
|
def remove(self, plex_id, plex_type=None):
|
||||||
|
@ -93,7 +97,7 @@ class MusicMixin(object):
|
||||||
path_id = self.kodicursor.fetchone()[0]
|
path_id = self.kodicursor.fetchone()[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
self.kodi_db.delete_song_from_song_artist(kodi_id)
|
self.kodidb.delete_song_from_song_artist(kodi_id)
|
||||||
self.kodicursor.execute('DELETE FROM song WHERE idSong = ?',
|
self.kodicursor.execute('DELETE FROM song WHERE idSong = ?',
|
||||||
(kodi_id, ))
|
(kodi_id, ))
|
||||||
# Check whether we have orphaned path entries
|
# Check whether we have orphaned path entries
|
||||||
|
@ -103,7 +107,7 @@ class MusicMixin(object):
|
||||||
self.kodicursor.execute('DELETE FROM path WHERE idPath = ?',
|
self.kodicursor.execute('DELETE FROM path WHERE idPath = ?',
|
||||||
(path_id, ))
|
(path_id, ))
|
||||||
if v.KODIVERSION < 18:
|
if v.KODIVERSION < 18:
|
||||||
self.kodi_db.delete_song_from_song_genre(kodi_id)
|
self.kodidb.delete_song_from_song_genre(kodi_id)
|
||||||
query = 'DELETE FROM albuminfosong WHERE idAlbumInfoSong = ?'
|
query = 'DELETE FROM albuminfosong WHERE idAlbumInfoSong = ?'
|
||||||
self.kodicursor.execute(query, (kodi_id, ))
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
artwork.delete_artwork(kodi_id, v.KODI_TYPE_SONG, self.kodicursor)
|
artwork.delete_artwork(kodi_id, v.KODI_TYPE_SONG, self.kodicursor)
|
||||||
|
@ -112,9 +116,9 @@ class MusicMixin(object):
|
||||||
'''
|
'''
|
||||||
Remove an album
|
Remove an album
|
||||||
'''
|
'''
|
||||||
self.kodi_db.delete_album_from_discography(kodi_id)
|
self.kodidb.delete_album_from_discography(kodi_id)
|
||||||
if v.KODIVERSION < 18:
|
if v.KODIVERSION < 18:
|
||||||
self.kodi_db.delete_album_from_album_genre(kodi_id)
|
self.kodidb.delete_album_from_album_genre(kodi_id)
|
||||||
query = 'DELETE FROM albuminfosong WHERE idAlbumInfo = ?'
|
query = 'DELETE FROM albuminfosong WHERE idAlbumInfo = ?'
|
||||||
self.kodicursor.execute(query, (kodi_id, ))
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
self.kodicursor.execute('DELETE FROM album_artist WHERE idAlbum = ?',
|
self.kodicursor.execute('DELETE FROM album_artist WHERE idAlbum = ?',
|
||||||
|
@ -183,7 +187,7 @@ class Artist(MusicMixin, ItemBase):
|
||||||
# multiple times.
|
# multiple times.
|
||||||
# Kodi doesn't allow that. In case that happens we just merge the
|
# Kodi doesn't allow that. In case that happens we just merge the
|
||||||
# artist entries.
|
# artist entries.
|
||||||
kodi_id = self.kodi_db.add_artist(api.title(), musicBrainzId)
|
kodi_id = self.kodidb.add_artist(api.title(), musicBrainzId)
|
||||||
# Create the reference in plex table
|
# Create the reference in plex table
|
||||||
query = '''
|
query = '''
|
||||||
UPDATE artist
|
UPDATE artist
|
||||||
|
@ -279,7 +283,7 @@ class Album(MusicMixin, ItemBase):
|
||||||
# OR ADD THE ALBUM #####
|
# OR ADD THE ALBUM #####
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD album plex_id: %s - Name: %s", plex_id, name)
|
LOG.info("ADD album plex_id: %s - Name: %s", plex_id, name)
|
||||||
kodi_id = self.kodi_db.add_album(name, musicBrainzId)
|
kodi_id = self.kodidb.add_album(name, musicBrainzId)
|
||||||
# Process the album info
|
# Process the album info
|
||||||
if v.KODIVERSION >= 18:
|
if v.KODIVERSION >= 18:
|
||||||
# Kodi Leia
|
# Kodi Leia
|
||||||
|
@ -360,7 +364,7 @@ class Album(MusicMixin, ItemBase):
|
||||||
self.kodicursor.execute(query,
|
self.kodicursor.execute(query,
|
||||||
(artist_id, name, api.year()))
|
(artist_id, name, api.year()))
|
||||||
if v.KODIVERSION < 18:
|
if v.KODIVERSION < 18:
|
||||||
self.kodi_db.add_music_genres(kodi_id,
|
self.kodidb.add_music_genres(kodi_id,
|
||||||
genres,
|
genres,
|
||||||
v.KODI_TYPE_ALBUM)
|
v.KODI_TYPE_ALBUM)
|
||||||
# Update artwork
|
# Update artwork
|
||||||
|
@ -379,7 +383,7 @@ class Album(MusicMixin, ItemBase):
|
||||||
if scan_children:
|
if scan_children:
|
||||||
context = Song(self.last_sync,
|
context = Song(self.last_sync,
|
||||||
plexdb=self.plexdb,
|
plexdb=self.plexdb,
|
||||||
kodi_db=self.kodi_db)
|
kodidb=self.kodidb)
|
||||||
for song in children:
|
for song in children:
|
||||||
context.add_update(song,
|
context.add_update(song,
|
||||||
section_name=section_name,
|
section_name=section_name,
|
||||||
|
@ -426,7 +430,7 @@ class Song(MusicMixin, ItemBase):
|
||||||
LOG.error('Grandparent tvartist %s xml download failed for %s',
|
LOG.error('Grandparent tvartist %s xml download failed for %s',
|
||||||
artist_id, xml.attrib)
|
artist_id, xml.attrib)
|
||||||
return
|
return
|
||||||
Artist(self.last_sync, plexdb=self.plexdb, kodi_db=self.kodi_db).add_update(
|
Artist(self.last_sync, plexdb=self.plexdb, kodidb=self.kodidb).add_update(
|
||||||
artist_xml[0], section_name, section_id)
|
artist_xml[0], section_name, section_id)
|
||||||
artist = self.plexdb.artist(artist_id)
|
artist = self.plexdb.artist(artist_id)
|
||||||
if not artist:
|
if not artist:
|
||||||
|
@ -463,7 +467,7 @@ class Song(MusicMixin, ItemBase):
|
||||||
LOG.error('Parent album %s xml download failed for %s',
|
LOG.error('Parent album %s xml download failed for %s',
|
||||||
album_id, xml.attrib)
|
album_id, xml.attrib)
|
||||||
return
|
return
|
||||||
Album(self.last_sync, plexdb=self.plexdb, kodi_db=self.kodi_db).add_update(
|
Album(self.last_sync, plexdb=self.plexdb, kodidb=self.kodidb).add_update(
|
||||||
album_xml[0], section_name, section_id)
|
album_xml[0], section_name, section_id)
|
||||||
album = self.plexdb.album(album_id)
|
album = self.plexdb.album(album_id)
|
||||||
if not album:
|
if not album:
|
||||||
|
@ -615,7 +619,7 @@ class Song(MusicMixin, ItemBase):
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD song plex_id: %s - %s", plex_id, title)
|
LOG.info("ADD song plex_id: %s - %s", plex_id, title)
|
||||||
# Add path
|
# Add path
|
||||||
kodi_pathid = self.kodi_db.add_music_path(path, hash_string="123")
|
kodi_pathid = self.kodidb.add_music_path(path, hash_string="123")
|
||||||
# Create the song entry
|
# Create the song entry
|
||||||
if v.KODIVERSION >= 18:
|
if v.KODIVERSION >= 18:
|
||||||
# Kodi Leia
|
# Kodi Leia
|
||||||
|
@ -730,7 +734,7 @@ class Song(MusicMixin, ItemBase):
|
||||||
(grandparent_id, kodi_id, 1, 0, artist_name))
|
(grandparent_id, kodi_id, 1, 0, artist_name))
|
||||||
# Add genres
|
# Add genres
|
||||||
if genres:
|
if genres:
|
||||||
self.kodi_db.add_music_genres(kodi_id, genres, v.KODI_TYPE_SONG)
|
self.kodidb.add_music_genres(kodi_id, genres, v.KODI_TYPE_SONG)
|
||||||
artworks = api.artwork()
|
artworks = api.artwork()
|
||||||
artwork.modify_artwork(artworks,
|
artwork.modify_artwork(artworks,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from logging import getLogger
|
||||||
|
|
||||||
from .common import ItemBase, process_path
|
from .common import ItemBase, process_path
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from .. import artwork, plex_functions as PF, state, variables as v
|
from .. import plex_functions as PF, state, variables as v
|
||||||
|
|
||||||
LOG = getLogger('PLEX.tvshows')
|
LOG = getLogger('PLEX.tvshows')
|
||||||
|
|
||||||
|
@ -74,38 +74,35 @@ class TvShowMixin(object):
|
||||||
"""
|
"""
|
||||||
Remove a TV show, and only the show, no seasons or episodes
|
Remove a TV show, and only the show, no seasons or episodes
|
||||||
"""
|
"""
|
||||||
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.modify_genres(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.modify_studios(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.modify_tags(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
artwork.delete_artwork(kodi_id, v.KODI_TYPE_SHOW, self.kodicursor)
|
self.kodidb.delete_artwork(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
self.kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?",
|
self.kodidb.remove_show(kodi_id)
|
||||||
(kodi_id,))
|
|
||||||
if v.KODIVERSION >= 17:
|
if v.KODIVERSION >= 17:
|
||||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.remove_ratings(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
LOG.debug("Removed tvshow: %s", kodi_id)
|
LOG.debug("Removed tvshow: %s", kodi_id)
|
||||||
|
|
||||||
def remove_season(self, kodi_id):
|
def remove_season(self, kodi_id):
|
||||||
"""
|
"""
|
||||||
Remove a season, and only a season, not the show or episodes
|
Remove a season, and only a season, not the show or episodes
|
||||||
"""
|
"""
|
||||||
artwork.delete_artwork(kodi_id, v.KODI_TYPE_SEASON, self.kodicursor)
|
self.kodidb.delete_artwork(kodi_id, v.KODI_TYPE_SEASON)
|
||||||
self.kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?",
|
self.kodidb.remove_season(kodi_id)
|
||||||
(kodi_id,))
|
|
||||||
LOG.debug("Removed season: %s", kodi_id)
|
LOG.debug("Removed season: %s", kodi_id)
|
||||||
|
|
||||||
def remove_episode(self, kodi_id, file_id):
|
def remove_episode(self, kodi_id, file_id):
|
||||||
"""
|
"""
|
||||||
Remove an episode, and episode only from the Kodi DB (not Plex DB)
|
Remove an episode, and episode only from the Kodi DB (not Plex DB)
|
||||||
"""
|
"""
|
||||||
self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
self.kodidb.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.remove_file(file_id, plex_type=v.PLEX_TYPE_EPISODE)
|
self.kodidb.remove_file(file_id, plex_type=v.PLEX_TYPE_EPISODE)
|
||||||
artwork.delete_artwork(kodi_id, v.KODI_TYPE_EPISODE, self.kodicursor)
|
self.kodidb.delete_artwork(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
self.kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?",
|
self.kodidb.remove_episode(kodi_id)
|
||||||
(kodi_id,))
|
|
||||||
if v.KODIVERSION >= 17:
|
if v.KODIVERSION >= 17:
|
||||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_EPISODE)
|
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_EPISODE)
|
self.kodidb.remove_ratings(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
LOG.debug("Removed episode: %s", kodi_id)
|
LOG.debug("Removed episode: %s", kodi_id)
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,23 +123,11 @@ class Show(ItemBase, TvShowMixin):
|
||||||
show = self.plexdb.show(plex_id)
|
show = self.plexdb.show(plex_id)
|
||||||
if not show:
|
if not show:
|
||||||
update_item = False
|
update_item = False
|
||||||
query = 'SELECT COALESCE(MAX(idShow), 0) FROM tvshow'
|
kodi_id = self.kodidb.new_show_id()
|
||||||
self.kodicursor.execute(query)
|
|
||||||
kodi_id = self.kodicursor.fetchone()[0] + 1
|
|
||||||
else:
|
else:
|
||||||
update_item = True
|
update_item = True
|
||||||
kodi_id = show['kodi_id']
|
kodi_id = show['kodi_id']
|
||||||
kodi_pathid = show['kodi_pathid']
|
kodi_pathid = show['kodi_pathid']
|
||||||
# Verification the item is still in Kodi
|
|
||||||
self.kodicursor.execute('SELECT * FROM tvshow WHERE idShow = ?',
|
|
||||||
(kodi_id,))
|
|
||||||
try:
|
|
||||||
self.kodicursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# item is not found, let's recreate it.
|
|
||||||
update_item = False
|
|
||||||
LOG.info("idShow: %s missing from Kodi, repairing the entry.",
|
|
||||||
kodi_id)
|
|
||||||
|
|
||||||
genres = api.genre_list()
|
genres = api.genre_list()
|
||||||
genre = api.list_to_string(genres)
|
genre = api.list_to_string(genres)
|
||||||
|
@ -158,7 +143,7 @@ class Show(ItemBase, TvShowMixin):
|
||||||
if playurl is None:
|
if playurl is None:
|
||||||
return
|
return
|
||||||
path, toplevelpath = process_path(playurl)
|
path, toplevelpath = process_path(playurl)
|
||||||
toppathid = self.kodi_db.add_video_path(
|
toppathid = self.kodidb.add_video_path(
|
||||||
toplevelpath,
|
toplevelpath,
|
||||||
content='tvshows',
|
content='tvshows',
|
||||||
scraper='metadata.local')
|
scraper='metadata.local')
|
||||||
|
@ -169,15 +154,15 @@ class Show(ItemBase, TvShowMixin):
|
||||||
# Do NOT set a parent id because addon-path cannot be "stacked"
|
# Do NOT set a parent id because addon-path cannot be "stacked"
|
||||||
toppathid = None
|
toppathid = None
|
||||||
|
|
||||||
kodi_pathid = self.kodi_db.add_video_path(path,
|
kodi_pathid = self.kodidb.add_video_path(path,
|
||||||
date_added=api.date_created(),
|
date_added=api.date_created(),
|
||||||
id_parent_path=toppathid)
|
id_parent_path=toppathid)
|
||||||
# UPDATE THE TVSHOW #####
|
# UPDATE THE TVSHOW #####
|
||||||
if update_item:
|
if update_item:
|
||||||
LOG.info("UPDATE tvshow plex_id: %s - %s", plex_id, api.title())
|
LOG.info("UPDATE tvshow plex_id: %s - %s", plex_id, api.title())
|
||||||
# update new ratings Kodi 17
|
# update new ratings Kodi 17
|
||||||
rating_id = self.kodi_db.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
rating_id = self.kodidb.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.update_ratings(kodi_id,
|
self.kodidb.update_ratings(kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW,
|
||||||
"default",
|
"default",
|
||||||
api.audience_rating(),
|
api.audience_rating(),
|
||||||
|
@ -185,45 +170,44 @@ class Show(ItemBase, TvShowMixin):
|
||||||
rating_id)
|
rating_id)
|
||||||
# update new uniqueid Kodi 17
|
# update new uniqueid Kodi 17
|
||||||
if api.provider('tvdb') is not None:
|
if api.provider('tvdb') is not None:
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_SHOW)
|
v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.update_uniqueid(kodi_id,
|
self.kodidb.update_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW,
|
||||||
api.provider('tvdb'),
|
api.provider('tvdb'),
|
||||||
"unknown",
|
"unknown",
|
||||||
uniqueid)
|
uniqueid)
|
||||||
else:
|
else:
|
||||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
uniqueid = -1
|
uniqueid = -1
|
||||||
# Update the tvshow entry
|
# Update the tvshow entry
|
||||||
query = '''
|
self.kodidb.update_show(api.title(),
|
||||||
UPDATE tvshow
|
api.plot(),
|
||||||
SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,
|
rating_id,
|
||||||
c12 = ?, c13 = ?, c14 = ?, c15 = ?
|
api.premiere_date(),
|
||||||
WHERE idShow = ?
|
genre,
|
||||||
'''
|
api.title(),
|
||||||
self.kodicursor.execute(
|
uniqueid,
|
||||||
query, (api.title(), api.plot(), rating_id,
|
api.content_rating(),
|
||||||
api.premiere_date(), genre, api.title(), uniqueid,
|
studio,
|
||||||
api.content_rating(), studio, api.sorttitle(),
|
api.sorttitle(),
|
||||||
kodi_id))
|
kodi_id)
|
||||||
# OR ADD THE TVSHOW #####
|
# OR ADD THE TVSHOW #####
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD tvshow plex_id: %s - %s", plex_id, api.title())
|
LOG.info("ADD tvshow plex_id: %s - %s", plex_id, api.title())
|
||||||
# Link the path
|
# Link the path
|
||||||
query = "INSERT INTO tvshowlinkpath(idShow, idPath) values (?, ?)"
|
self.kodidb.add_showlinkpath(kodi_id, kodi_pathid)
|
||||||
self.kodicursor.execute(query, (kodi_id, kodi_pathid))
|
rating_id = self.kodidb.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
rating_id = self.kodi_db.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
self.kodidb.add_ratings(rating_id,
|
||||||
self.kodi_db.add_ratings(rating_id,
|
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW,
|
||||||
"default",
|
"default",
|
||||||
api.audience_rating(),
|
api.audience_rating(),
|
||||||
api.votecount())
|
api.votecount())
|
||||||
if api.provider('tvdb') is not None:
|
if api.provider('tvdb') is not None:
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_SHOW)
|
v.KODI_TYPE_SHOW)
|
||||||
self.kodi_db.add_uniqueid(uniqueid,
|
self.kodidb.add_uniqueid(uniqueid,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW,
|
||||||
api.provider('tvdb'),
|
api.provider('tvdb'),
|
||||||
|
@ -231,31 +215,30 @@ class Show(ItemBase, TvShowMixin):
|
||||||
else:
|
else:
|
||||||
uniqueid = -1
|
uniqueid = -1
|
||||||
# Create the tvshow entry
|
# Create the tvshow entry
|
||||||
query = '''
|
self.kodidb.add_show(kodi_id,
|
||||||
INSERT INTO tvshow(
|
api.title(),
|
||||||
idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14,
|
api.plot(),
|
||||||
c15)
|
rating_id,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
api.premiere_date(),
|
||||||
'''
|
genre,
|
||||||
self.kodicursor.execute(
|
api.title(),
|
||||||
query, (kodi_id, api.title(), api.plot(), rating_id,
|
uniqueid,
|
||||||
api.premiere_date(), genre, api.title(), uniqueid,
|
api.content_rating(),
|
||||||
api.content_rating(), studio, api.sorttitle()))
|
studio,
|
||||||
|
api.sorttitle())
|
||||||
self.kodi_db.modify_people(kodi_id,
|
self.kodidb.modify_people(kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW,
|
||||||
api.people_list())
|
api.people_list())
|
||||||
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW, genres)
|
self.kodidb.modify_genres(kodi_id, v.KODI_TYPE_SHOW, genres)
|
||||||
artwork.modify_artwork(api.artwork(),
|
self.kodidb.modify_artwork(api.artwork(),
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW)
|
||||||
self.kodicursor)
|
|
||||||
# Process studios
|
# Process studios
|
||||||
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW, studios)
|
self.kodidb.modify_studios(kodi_id, v.KODI_TYPE_SHOW, studios)
|
||||||
# Process tags: view, PMS collection tags
|
# Process tags: view, PMS collection tags
|
||||||
tags = [section_name]
|
tags = [section_name]
|
||||||
tags.extend([i for _, i in api.collection_list()])
|
tags.extend([i for _, i in api.collection_list()])
|
||||||
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW, tags)
|
self.kodidb.modify_tags(kodi_id, v.KODI_TYPE_SHOW, tags)
|
||||||
self.plexdb.add_show(plex_id=plex_id,
|
self.plexdb.add_show(plex_id=plex_id,
|
||||||
checksum=api.checksum(),
|
checksum=api.checksum(),
|
||||||
section_id=section_id,
|
section_id=section_id,
|
||||||
|
@ -287,18 +270,17 @@ class Season(ItemBase, TvShowMixin):
|
||||||
except (TypeError, IndexError, AttributeError):
|
except (TypeError, IndexError, AttributeError):
|
||||||
LOG.error("Parent tvshow %s xml download failed", show_id)
|
LOG.error("Parent tvshow %s xml download failed", show_id)
|
||||||
return False
|
return False
|
||||||
Show(self.last_sync, plexdb=self.plexdb, kodi_db=self.kodi_db).add_update(
|
Show(self.last_sync, plexdb=self.plexdb, kodidb=self.kodidb).add_update(
|
||||||
show_xml[0], section_name, section_id)
|
show_xml[0], section_name, section_id)
|
||||||
show = self.plexdb.show(show_id)
|
show = self.plexdb.show(show_id)
|
||||||
if not show:
|
if not show:
|
||||||
LOG.error('Still could not find parent tv show %s', show_id)
|
LOG.error('Still could not find parent tv show %s', show_id)
|
||||||
return
|
return
|
||||||
parent_id = show['kodi_id']
|
parent_id = show['kodi_id']
|
||||||
kodi_id = self.kodi_db.add_season(parent_id, api.season_number())
|
kodi_id = self.kodidb.add_season(parent_id, api.season_number())
|
||||||
artwork.modify_artwork(api.artwork(),
|
self.kodidb.modify_artwork(api.artwork(),
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_SEASON,
|
v.KODI_TYPE_SEASON)
|
||||||
self.kodicursor)
|
|
||||||
self.plexdb.add_season(plex_id=plex_id,
|
self.plexdb.add_season(plex_id=plex_id,
|
||||||
checksum=api.checksum(),
|
checksum=api.checksum(),
|
||||||
section_id=section_id,
|
section_id=section_id,
|
||||||
|
@ -323,24 +305,12 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
episode = self.plexdb.episode(plex_id)
|
episode = self.plexdb.episode(plex_id)
|
||||||
if not episode:
|
if not episode:
|
||||||
update_item = False
|
update_item = False
|
||||||
query = 'SELECT COALESCE(MAX(idEpisode), 0) FROM episode'
|
kodi_id = self.kodidb.new_episode_id()
|
||||||
self.kodicursor.execute(query)
|
|
||||||
kodi_id = self.kodicursor.fetchone()[0] + 1
|
|
||||||
else:
|
else:
|
||||||
update_item = True
|
update_item = True
|
||||||
kodi_id = episode['kodi_id']
|
kodi_id = episode['kodi_id']
|
||||||
old_kodi_fileid = episode['kodi_fileid']
|
old_kodi_fileid = episode['kodi_fileid']
|
||||||
kodi_pathid = episode['kodi_pathid']
|
kodi_pathid = episode['kodi_pathid']
|
||||||
# Verification the item is still in Kodi
|
|
||||||
query = 'SELECT * FROM episode WHERE idEpisode = ? LIMIT 1'
|
|
||||||
self.kodicursor.execute(query, (kodi_id, ))
|
|
||||||
try:
|
|
||||||
self.kodicursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# item is not found, let's recreate it.
|
|
||||||
update_item = False
|
|
||||||
LOG.info('idEpisode %s missing from Kodi, repairing entry.',
|
|
||||||
kodi_id)
|
|
||||||
|
|
||||||
peoples = api.people()
|
peoples = api.people()
|
||||||
director = api.list_to_string(peoples['Director'])
|
director = api.list_to_string(peoples['Director'])
|
||||||
|
@ -365,7 +335,7 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
except (TypeError, IndexError, AttributeError):
|
except (TypeError, IndexError, AttributeError):
|
||||||
LOG.error("Grandparent tvshow %s xml download failed", show_id)
|
LOG.error("Grandparent tvshow %s xml download failed", show_id)
|
||||||
return False
|
return False
|
||||||
Show(self.last_sync, plexdb=self.plexdb, kodi_db=self.kodi_db).add_update(
|
Show(self.last_sync, plexdb=self.plexdb, kodidb=self.kodidb).add_update(
|
||||||
show_xml[0], section_name, section_id)
|
show_xml[0], section_name, section_id)
|
||||||
show = self.plexdb.show(show_id)
|
show = self.plexdb.show(show_id)
|
||||||
if not show:
|
if not show:
|
||||||
|
@ -383,7 +353,7 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
except (TypeError, IndexError, AttributeError):
|
except (TypeError, IndexError, AttributeError):
|
||||||
LOG.error("Parent season %s xml download failed", season_id)
|
LOG.error("Parent season %s xml download failed", season_id)
|
||||||
return False
|
return False
|
||||||
Season(self.last_sync, plexdb=self.plexdb, kodi_db=self.kodi_db).add_update(
|
Season(self.last_sync, plexdb=self.plexdb, kodidb=self.kodidb).add_update(
|
||||||
season_xml[0], section_name, section_id)
|
season_xml[0], section_name, section_id)
|
||||||
season = self.plexdb.season(season_id)
|
season = self.plexdb.season(season_id)
|
||||||
if not season:
|
if not season:
|
||||||
|
@ -406,8 +376,8 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
# Network share
|
# Network share
|
||||||
filename = playurl.rsplit("/", 1)[1]
|
filename = playurl.rsplit("/", 1)[1]
|
||||||
path = playurl.replace(filename, "")
|
path = playurl.replace(filename, "")
|
||||||
parent_path_id = self.kodi_db.parent_path_id(path)
|
parent_path_id = self.kodidb.parent_path_id(path)
|
||||||
kodi_pathid = self.kodi_db.add_video_path(
|
kodi_pathid = self.kodidb.add_video_path(
|
||||||
path, id_parent_path=parent_path_id)
|
path, id_parent_path=parent_path_id)
|
||||||
if do_indirect:
|
if do_indirect:
|
||||||
# Set plugin path - do NOT use "intermediate" paths for the show
|
# Set plugin path - do NOT use "intermediate" paths for the show
|
||||||
|
@ -418,11 +388,11 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
% (path, plex_id, v.PLEX_TYPE_EPISODE, filename))
|
% (path, plex_id, v.PLEX_TYPE_EPISODE, filename))
|
||||||
playurl = filename
|
playurl = filename
|
||||||
# Root path tvshows/ already saved in Kodi DB
|
# Root path tvshows/ already saved in Kodi DB
|
||||||
kodi_pathid = self.kodi_db.add_video_path(path)
|
kodi_pathid = self.kodidb.add_video_path(path)
|
||||||
|
|
||||||
# add/retrieve kodi_pathid and fileid
|
# add/retrieve kodi_pathid and fileid
|
||||||
# if the path or file already exists, the calls return current value
|
# if the path or file already exists, the calls return current value
|
||||||
kodi_fileid = self.kodi_db.add_file(filename,
|
kodi_fileid = self.kodidb.add_file(filename,
|
||||||
kodi_pathid,
|
kodi_pathid,
|
||||||
api.date_created())
|
api.date_created())
|
||||||
|
|
||||||
|
@ -430,82 +400,91 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
if update_item:
|
if update_item:
|
||||||
LOG.info("UPDATE episode plex_id: %s - %s", plex_id, api.title())
|
LOG.info("UPDATE episode plex_id: %s - %s", plex_id, api.title())
|
||||||
if kodi_fileid != old_kodi_fileid:
|
if kodi_fileid != old_kodi_fileid:
|
||||||
self.kodi_db.remove_file(old_kodi_fileid)
|
self.kodidb.remove_file(old_kodi_fileid)
|
||||||
ratingid = self.kodi_db.get_ratingid(kodi_id,
|
ratingid = self.kodidb.get_ratingid(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE)
|
v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.update_ratings(kodi_id,
|
self.kodidb.update_ratings(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
"default",
|
"default",
|
||||||
userdata['Rating'],
|
userdata['Rating'],
|
||||||
api.votecount(),
|
api.votecount(),
|
||||||
ratingid)
|
ratingid)
|
||||||
# update new uniqueid Kodi 17
|
# update new uniqueid Kodi 17
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE)
|
v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.update_uniqueid(kodi_id,
|
self.kodidb.update_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
api.provider('tvdb'),
|
api.provider('tvdb'),
|
||||||
"tvdb",
|
"tvdb",
|
||||||
uniqueid)
|
uniqueid)
|
||||||
query = '''
|
self.kodidb.update_episode(api.title(),
|
||||||
UPDATE episode
|
api.plot(),
|
||||||
SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?,
|
ratingid,
|
||||||
c10 = ?, c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?,
|
writer,
|
||||||
c18 = ?, c19 = ?, idFile=?, idSeason = ?,
|
api.premiere_date(),
|
||||||
userrating = ?
|
api.runtime(),
|
||||||
WHERE idEpisode = ?
|
director,
|
||||||
'''
|
season_no,
|
||||||
self.kodicursor.execute(
|
episode_no,
|
||||||
query, (api.title(), api.plot(), ratingid, writer,
|
api.title(),
|
||||||
api.premiere_date(), api.runtime(), director, season_no,
|
airs_before_season,
|
||||||
episode_no, api.title(), airs_before_season,
|
airs_before_episode,
|
||||||
airs_before_episode, playurl, kodi_pathid, kodi_fileid,
|
playurl,
|
||||||
parent_id, userdata['UserRating'], kodi_id))
|
kodi_pathid,
|
||||||
|
kodi_fileid,
|
||||||
|
parent_id,
|
||||||
|
userdata['UserRating'],
|
||||||
|
kodi_id)
|
||||||
|
|
||||||
# OR ADD THE EPISODE #####
|
# OR ADD THE EPISODE #####
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD episode plex_id: %s - %s", plex_id, api.title())
|
LOG.info("ADD episode plex_id: %s - %s", plex_id, api.title())
|
||||||
# Create the episode entry
|
# Create the episode entry
|
||||||
rating_id = self.kodi_db.get_ratingid(kodi_id,
|
rating_id = self.kodidb.get_ratingid(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE)
|
v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.add_ratings(rating_id,
|
self.kodidb.add_ratings(rating_id,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
"default",
|
"default",
|
||||||
userdata['Rating'],
|
userdata['Rating'],
|
||||||
api.votecount())
|
api.votecount())
|
||||||
# add new uniqueid Kodi 17
|
# add new uniqueid Kodi 17
|
||||||
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
uniqueid = self.kodidb.get_uniqueid(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE)
|
v.KODI_TYPE_EPISODE)
|
||||||
self.kodi_db.add_uniqueid(uniqueid,
|
self.kodidb.add_uniqueid(uniqueid,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
api.provider('tvdb'),
|
api.provider('tvdb'),
|
||||||
"tvdb")
|
"tvdb")
|
||||||
query = '''
|
self.kodidb.add_episode(kodi_id,
|
||||||
INSERT INTO episode( idEpisode, idFile, c00, c01, c03, c04,
|
kodi_fileid,
|
||||||
c05, c09, c10, c12, c13, c14, idShow, c15, c16, c18,
|
api.title(),
|
||||||
c19, idSeason, userrating)
|
api.plot(),
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
rating_id,
|
||||||
?, ?)
|
writer,
|
||||||
'''
|
api.premiere_date(),
|
||||||
self.kodicursor.execute(
|
api.runtime(),
|
||||||
query, (kodi_id, kodi_fileid, api.title(), api.plot(), rating_id,
|
director,
|
||||||
writer, api.premiere_date(), api.runtime(), director,
|
season_no,
|
||||||
season_no, episode_no, api.title(), grandparent_id,
|
episode_no,
|
||||||
airs_before_season, airs_before_episode, playurl,
|
api.title(),
|
||||||
kodi_pathid, parent_id, userdata['UserRating']))
|
grandparent_id,
|
||||||
|
airs_before_season,
|
||||||
|
airs_before_episode,
|
||||||
|
playurl,
|
||||||
|
kodi_pathid,
|
||||||
|
parent_id,
|
||||||
|
userdata['UserRating'])
|
||||||
|
|
||||||
self.kodi_db.modify_people(kodi_id,
|
self.kodidb.modify_people(kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE,
|
||||||
api.people_list())
|
api.people_list())
|
||||||
artwork.modify_artwork(api.artwork(),
|
self.kodidb.modify_artwork(api.artwork(),
|
||||||
kodi_id,
|
kodi_id,
|
||||||
v.KODI_TYPE_EPISODE,
|
v.KODI_TYPE_EPISODE)
|
||||||
self.kodicursor)
|
|
||||||
streams = api.mediastreams()
|
streams = api.mediastreams()
|
||||||
self.kodi_db.modify_streams(kodi_fileid, streams, api.runtime())
|
self.kodidb.modify_streams(kodi_fileid, streams, api.runtime())
|
||||||
self.kodi_db.set_resume(kodi_fileid,
|
self.kodidb.set_resume(kodi_fileid,
|
||||||
api.resume_point(),
|
api.resume_point(),
|
||||||
api.runtime(),
|
api.runtime(),
|
||||||
userdata['PlayCount'],
|
userdata['PlayCount'],
|
||||||
|
@ -519,11 +498,11 @@ class Episode(ItemBase, TvShowMixin):
|
||||||
filename = ('%s%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
filename = ('%s%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
% (path, show_id, plex_id, v.PLEX_TYPE_EPISODE,
|
% (path, show_id, plex_id, v.PLEX_TYPE_EPISODE,
|
||||||
filename))
|
filename))
|
||||||
kodi_pathid = self.kodi_db.add_video_path(path)
|
kodi_pathid = self.kodidb.add_video_path(path)
|
||||||
kodi_fileid = self.kodi_db.add_file(filename,
|
kodi_fileid = self.kodidb.add_file(filename,
|
||||||
kodi_pathid,
|
kodi_pathid,
|
||||||
api.date_created())
|
api.date_created())
|
||||||
self.kodi_db.set_resume(kodi_fileid,
|
self.kodidb.set_resume(kodi_fileid,
|
||||||
api.resume_point(),
|
api.resume_point(),
|
||||||
api.runtime(),
|
api.runtime(),
|
||||||
userdata['PlayCount'],
|
userdata['PlayCount'],
|
||||||
|
|
129
resources/lib/kodi_db/__init__.py
Normal file
129
resources/lib/kodi_db/__init__.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from .video import KodiVideoDB
|
||||||
|
from .music import KodiMusicDB
|
||||||
|
from .texture import KodiTextureDB
|
||||||
|
|
||||||
|
from .. import path_ops, utils, variables as v
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.kodi_db')
|
||||||
|
|
||||||
|
|
||||||
|
def kodiid_from_filename(path, kodi_type=None, db_type=None):
|
||||||
|
"""
|
||||||
|
Returns kodi_id if we have an item in the Kodi video or audio database with
|
||||||
|
said path. Feed with either koditype, e.v. 'movie', 'song' or the DB
|
||||||
|
you want to poll ('video' or 'music')
|
||||||
|
Returns None, <kodi_type> if not possible
|
||||||
|
"""
|
||||||
|
kodi_id = None
|
||||||
|
path = utils.try_decode(path)
|
||||||
|
try:
|
||||||
|
filename = path.rsplit('/', 1)[1]
|
||||||
|
path = path.rsplit('/', 1)[0] + '/'
|
||||||
|
except IndexError:
|
||||||
|
filename = path.rsplit('\\', 1)[1]
|
||||||
|
path = path.rsplit('\\', 1)[0] + '\\'
|
||||||
|
if kodi_type == v.KODI_TYPE_SONG or db_type == 'music':
|
||||||
|
with KodiMusicDB() as kodidb:
|
||||||
|
try:
|
||||||
|
kodi_id = kodidb.music_id_from_filename(filename, path)
|
||||||
|
except TypeError:
|
||||||
|
LOG.debug('No Kodi audio db element found for path %s', path)
|
||||||
|
else:
|
||||||
|
kodi_type = v.KODI_TYPE_SONG
|
||||||
|
else:
|
||||||
|
with KodiVideoDB() as kodidb:
|
||||||
|
try:
|
||||||
|
kodi_id, kodi_type = kodidb.video_id_from_filename(filename,
|
||||||
|
path)
|
||||||
|
except TypeError:
|
||||||
|
LOG.debug('No kodi video db element found for path %s', path)
|
||||||
|
return kodi_id, kodi_type
|
||||||
|
|
||||||
|
|
||||||
|
def setup_kodi_default_entries():
|
||||||
|
"""
|
||||||
|
Makes sure that we retain the Kodi standard databases. E.g. that there
|
||||||
|
is a dummy artist with ID 1
|
||||||
|
"""
|
||||||
|
if utils.settings('enableMusic') == 'true':
|
||||||
|
with KodiMusicDB() as kodidb:
|
||||||
|
kodidb.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO artist(
|
||||||
|
idArtist,
|
||||||
|
strArtist,
|
||||||
|
strMusicBrainzArtistID)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', (1, '[Missing Tag]', 'Artist Tag Missing'))
|
||||||
|
if v.KODIVERSION >= 18:
|
||||||
|
kodidb.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO versiontagscan(
|
||||||
|
idVersion,
|
||||||
|
iNeedsScan,
|
||||||
|
lastscanned)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', (v.DB_MUSIC_VERSION[v.KODIVERSION],
|
||||||
|
0,
|
||||||
|
utils.unix_date_to_kodi(utils.unix_timestamp())))
|
||||||
|
|
||||||
|
|
||||||
|
def reset_cached_images():
|
||||||
|
LOG.info('Resetting cached artwork')
|
||||||
|
# Remove all existing textures first
|
||||||
|
path = path_ops.translate_path('special://thumbnails/')
|
||||||
|
if path_ops.exists(path):
|
||||||
|
path_ops.rmtree(path, ignore_errors=True)
|
||||||
|
paths = ('', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
|
'Video', 'plex')
|
||||||
|
for path in paths:
|
||||||
|
new_path = path_ops.translate_path('special://thumbnails/%s' % path)
|
||||||
|
path_ops.makedirs(path_ops.encode_path(new_path))
|
||||||
|
with KodiTextureDB() as kodidb:
|
||||||
|
for row in kodidb.cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type=?',
|
||||||
|
('table', )):
|
||||||
|
if row[0] != 'version':
|
||||||
|
kodidb.cursor.execute("DELETE FROM %s" % row[0])
|
||||||
|
|
||||||
|
|
||||||
|
def wipe_dbs(music=True):
|
||||||
|
"""
|
||||||
|
Completely resets the Kodi databases 'video', 'texture' and 'music' (if
|
||||||
|
music sync is enabled)
|
||||||
|
"""
|
||||||
|
LOG.warn('Wiping Kodi databases!')
|
||||||
|
kinds = [KodiVideoDB, KodiTextureDB]
|
||||||
|
if music:
|
||||||
|
kinds.append(KodiMusicDB)
|
||||||
|
for db in kinds:
|
||||||
|
with db() as kodidb:
|
||||||
|
kodidb.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
|
||||||
|
tables = kodidb.cursor.fetchall()
|
||||||
|
tables = [i[0] for i in tables]
|
||||||
|
if 'version' in tables:
|
||||||
|
tables.remove('version')
|
||||||
|
if 'versiontagscan' in tables:
|
||||||
|
tables.remove('versiontagscan')
|
||||||
|
for table in tables:
|
||||||
|
kodidb.cursor.execute('DELETE FROM %s' % table)
|
||||||
|
setup_kodi_default_entries()
|
||||||
|
# Make sure Kodi knows we wiped the databases
|
||||||
|
import xbmc
|
||||||
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
|
if utils.settings('enableMusic') == 'true':
|
||||||
|
xbmc.executebuiltin('UpdateLibrary(music)')
|
||||||
|
|
||||||
|
|
||||||
|
KODIDB_FROM_PLEXTYPE = {
|
||||||
|
v.PLEX_TYPE_MOVIE: KodiVideoDB,
|
||||||
|
v.PLEX_TYPE_SHOW: KodiVideoDB,
|
||||||
|
v.PLEX_TYPE_SEASON: KodiVideoDB,
|
||||||
|
v.PLEX_TYPE_EPISODE: KodiVideoDB,
|
||||||
|
v.PLEX_TYPE_ARTIST: KodiMusicDB,
|
||||||
|
v.PLEX_TYPE_ALBUM: KodiMusicDB,
|
||||||
|
v.PLEX_TYPE_SONG: KodiMusicDB
|
||||||
|
}
|
102
resources/lib/kodi_db/common.py
Normal file
102
resources/lib/kodi_db/common.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
|
from .. import utils, path_ops
|
||||||
|
|
||||||
|
|
||||||
|
class KodiDBBase(object):
|
||||||
|
"""
|
||||||
|
Kodi database methods used for all types of items
|
||||||
|
"""
|
||||||
|
def __init__(self, texture_db=False, cursor=None, artcursor=None):
|
||||||
|
"""
|
||||||
|
Allows direct use with a cursor instead of context mgr
|
||||||
|
"""
|
||||||
|
self._texture_db = texture_db
|
||||||
|
self.cursor = cursor
|
||||||
|
self.artconn = None
|
||||||
|
self.artcursor = artcursor
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.kodiconn = utils.kodi_sql(self.db_kind)
|
||||||
|
self.cursor = self.kodiconn.cursor()
|
||||||
|
if self._texture_db:
|
||||||
|
self.artconn = utils.kodi_sql('texture')
|
||||||
|
self.artcursor = self.artconn.cursor()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, e_typ, e_val, trcbak):
|
||||||
|
self.kodiconn.commit()
|
||||||
|
self.kodiconn.close()
|
||||||
|
if self._texture_db:
|
||||||
|
self.artconn.commit()
|
||||||
|
self.artconn.close()
|
||||||
|
|
||||||
|
def art_urls(self, kodi_id, kodi_type):
|
||||||
|
return (x[0] for x in
|
||||||
|
self.cursor.execute('SELECT url FROM art WHERE media_id = ? AND media_type = ?',
|
||||||
|
(kodi_id, kodi_type)))
|
||||||
|
|
||||||
|
def artwork_generator(self, kodi_type):
|
||||||
|
return (x[0] for x in
|
||||||
|
self.cursor.execute('SELECT url FROM art WHERE type == ?',
|
||||||
|
(kodi_type, )))
|
||||||
|
|
||||||
|
def modify_artwork(self, artworks, kodi_id, kodi_type):
|
||||||
|
"""
|
||||||
|
Pass in an artworks dict (see PlexAPI) to set an items artwork.
|
||||||
|
"""
|
||||||
|
for kodi_art, url in artworks.iteritems():
|
||||||
|
self.modify_art(url, kodi_id, kodi_type, kodi_art)
|
||||||
|
|
||||||
|
def modify_art(self, url, kodi_id, kodi_type, kodi_art):
|
||||||
|
"""
|
||||||
|
Adds or modifies the artwork of kind kodi_art (e.g. 'poster') in the
|
||||||
|
Kodi art table for item kodi_id/kodi_type. Will also cache everything
|
||||||
|
except actor portraits.
|
||||||
|
"""
|
||||||
|
self.cursor.execute('''
|
||||||
|
SELECT url FROM art
|
||||||
|
WHERE media_id = ? AND media_type = ? AND type = ?
|
||||||
|
LIMIT 1
|
||||||
|
''', (kodi_id, kodi_type, kodi_art,))
|
||||||
|
try:
|
||||||
|
# Update the artwork
|
||||||
|
old_url = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Add the artwork
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT INTO art(media_id, media_type, type, url)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
''', (kodi_id, kodi_type, kodi_art, url))
|
||||||
|
else:
|
||||||
|
if url == old_url:
|
||||||
|
# Only cache artwork if it changed
|
||||||
|
return
|
||||||
|
self.delete_cached_artwork(old_url)
|
||||||
|
self.cursor.execute('''
|
||||||
|
UPDATE art SET url = ?
|
||||||
|
WHERE media_id = ? AND media_type = ? AND type = ?
|
||||||
|
''', (url, kodi_id, kodi_type, kodi_art))
|
||||||
|
|
||||||
|
def delete_artwork(self, kodi_id, kodi_type):
|
||||||
|
for row in self.cursor.execute('SELECT url FROM art WHERE media_id = ? AND media_type = ?',
|
||||||
|
(kodi_id, kodi_type, )):
|
||||||
|
self.delete_cached_artwork(row[0])
|
||||||
|
|
||||||
|
def delete_cached_artwork(self, url):
|
||||||
|
try:
|
||||||
|
self.artcursor.execute("SELECT cachedurl FROM texture WHERE url = ? LIMIT 1",
|
||||||
|
(url, ))
|
||||||
|
cachedurl = self.artcursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Could not find cached url
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Delete thumbnail as well as the entry
|
||||||
|
path = path_ops.translate_path("special://thumbnails/%s"
|
||||||
|
% cachedurl)
|
||||||
|
if path_ops.exists(path):
|
||||||
|
path_ops.rmtree(path, ignore_errors=True)
|
||||||
|
self.artcursor.execute("DELETE FROM texture WHERE url = ?", (url, ))
|
11
resources/lib/kodi_db/movies.py
Normal file
11
resources/lib/kodi_db/movies.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from .video import KodiVideoDB
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.kodi_db.movies')
|
||||||
|
|
||||||
|
|
||||||
|
class KodiMovieDB(KodiVideoDB):
|
247
resources/lib/kodi_db/music.py
Normal file
247
resources/lib/kodi_db/music.py
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
from .. import utils, variables as v
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.kodi_db.music')
|
||||||
|
|
||||||
|
|
||||||
|
class KodiMusicDB(common.KodiDBBase):
|
||||||
|
db_kind = 'music'
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.kodiconn = utils.kodi_sql('music')
|
||||||
|
self.cursor = self.kodiconn.cursor()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_music_path(self, path, hash_string=None):
|
||||||
|
"""
|
||||||
|
Add the path (unicode) to the music DB, if it does not exist already.
|
||||||
|
Returns the path id
|
||||||
|
Set hash_string to something unicode to set the strHash attribute
|
||||||
|
"""
|
||||||
|
# SQL won't return existing paths otherwise
|
||||||
|
path = '' if path is None else path
|
||||||
|
self.cursor.execute('SELECT idPath FROM path WHERE strPath = ?',
|
||||||
|
(path,))
|
||||||
|
try:
|
||||||
|
pathid = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
self.cursor.execute("SELECT COALESCE(MAX(idPath),0) FROM path")
|
||||||
|
pathid = self.cursor.fetchone()[0] + 1
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT INTO path(idPath, strPath, strHash)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''',
|
||||||
|
(pathid, path, hash_string))
|
||||||
|
return pathid
|
||||||
|
|
||||||
|
def music_id_from_filename(self, filename, path):
|
||||||
|
"""
|
||||||
|
Returns the Kodi song_id from the Kodi music database or None if not
|
||||||
|
found OR something went wrong.
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT idPath FROM path WHERE strPath = ?',
|
||||||
|
(path,))
|
||||||
|
path_ids = self.cursor.fetchall()
|
||||||
|
if len(path_ids) != 1:
|
||||||
|
LOG.debug('Found wrong number of path ids: %s for path %s, abort',
|
||||||
|
path_ids, path)
|
||||||
|
return
|
||||||
|
self.cursor.execute('SELECT idSong FROM song WHERE strFileName = ? AND idPath = ?',
|
||||||
|
(filename, path_ids[0][0]))
|
||||||
|
song_ids = self.cursor.fetchall()
|
||||||
|
if len(song_ids) != 1:
|
||||||
|
LOG.info('Found wrong number of songs %s, abort', song_ids)
|
||||||
|
return
|
||||||
|
return song_ids[0][0]
|
||||||
|
|
||||||
|
def delete_song_from_song_artist(self, song_id):
|
||||||
|
"""
|
||||||
|
Deletes son from song_artist table and possibly orphaned roles
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT idArtist, idRole FROM song_artist WHERE idSong = ? LIMIT 1',
|
||||||
|
(song_id, ))
|
||||||
|
artist = self.cursor.fetchone()
|
||||||
|
if artist is None:
|
||||||
|
# No entry to begin with
|
||||||
|
return
|
||||||
|
# Delete the entry
|
||||||
|
self.cursor.execute('DELETE FROM song_artist WHERE idSong = ?',
|
||||||
|
(song_id, ))
|
||||||
|
# Check whether we need to delete orphaned roles
|
||||||
|
self.cursor.execute('SELECT idRole FROM song_artist WHERE idRole = ? LIMIT 1',
|
||||||
|
(artist[1], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
# Delete orphaned role
|
||||||
|
self.cursor.execute('DELETE FROM role WHERE idRole = ?',
|
||||||
|
(artist[1], ))
|
||||||
|
|
||||||
|
def delete_album_from_discography(self, album_id):
|
||||||
|
"""
|
||||||
|
Removes the album with id album_id from the table discography
|
||||||
|
"""
|
||||||
|
# Need to get the album name as a string first!
|
||||||
|
self.cursor.execute('SELECT strAlbum, iYear FROM album WHERE idAlbum = ? LIMIT 1',
|
||||||
|
(album_id, ))
|
||||||
|
try:
|
||||||
|
name, year = self.cursor.fetchone()
|
||||||
|
except TypeError:
|
||||||
|
return
|
||||||
|
self.cursor.execute('SELECT idArtist FROM album_artist WHERE idAlbum = ? LIMIT 1',
|
||||||
|
(album_id, ))
|
||||||
|
artist = self.cursor.fetchone()
|
||||||
|
if not artist:
|
||||||
|
return
|
||||||
|
self.cursor.execute('DELETE FROM discography WHERE idArtist = ? AND strAlbum = ? AND strYear = ?',
|
||||||
|
(artist[0], name, year))
|
||||||
|
|
||||||
|
def delete_song_from_song_genre(self, song_id):
|
||||||
|
"""
|
||||||
|
Deletes the one entry with id song_id from the song_genre table.
|
||||||
|
Will also delete orphaned genres from genre table
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idSong = ?',
|
||||||
|
(song_id, ))
|
||||||
|
genres = self.cursor.fetchall()
|
||||||
|
self.cursor.execute('DELETE FROM song_genre WHERE idSong = ?',
|
||||||
|
(song_id, ))
|
||||||
|
# Check for orphaned genres in both song_genre and album_genre tables
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1',
|
||||||
|
(genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1',
|
||||||
|
(genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
||||||
|
(genre[0], ))
|
||||||
|
|
||||||
|
def delete_album_from_album_genre(self, album_id):
|
||||||
|
"""
|
||||||
|
Deletes the one entry with id album_id from the album_genre table.
|
||||||
|
Will also delete orphaned genres from genre table
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idAlbum = ?',
|
||||||
|
(album_id, ))
|
||||||
|
genres = self.cursor.fetchall()
|
||||||
|
self.cursor.execute('DELETE FROM album_genre WHERE idAlbum = ?',
|
||||||
|
(album_id, ))
|
||||||
|
# Check for orphaned genres in both album_genre and song_genre tables
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1',
|
||||||
|
(genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1',
|
||||||
|
(genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
||||||
|
(genre[0], ))
|
||||||
|
|
||||||
|
def add_album(self, name, musicbrainz):
|
||||||
|
"""
|
||||||
|
Adds a single album to the DB
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?',
|
||||||
|
(musicbrainz, ))
|
||||||
|
try:
|
||||||
|
albumid = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Create the album
|
||||||
|
self.cursor.execute('SELECT COALESCE(MAX(idAlbum),0) FROM album')
|
||||||
|
albumid = self.cursor.fetchone()[0] + 1
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT INTO album(
|
||||||
|
idAlbum,
|
||||||
|
strAlbum,
|
||||||
|
strMusicBrainzAlbumID,
|
||||||
|
strReleaseType)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
''', (albumid, name, musicbrainz, 'album'))
|
||||||
|
return albumid
|
||||||
|
|
||||||
|
def add_music_genres(self, kodiid, genres, mediatype):
|
||||||
|
"""
|
||||||
|
Adds a list of genres (list of unicode) for a certain Kodi item
|
||||||
|
"""
|
||||||
|
if mediatype == "album":
|
||||||
|
# Delete current genres for clean slate
|
||||||
|
self.cursor.execute('DELETE FROM album_genre WHERE idAlbum = ?',
|
||||||
|
(kodiid, ))
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute('SELECT idGenre FROM genre WHERE strGenre = ?',
|
||||||
|
(genre, ))
|
||||||
|
try:
|
||||||
|
genreid = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Create the genre
|
||||||
|
self.cursor.execute('SELECT COALESCE(MAX(idGenre),0) FROM genre')
|
||||||
|
genreid = self.cursor.fetchone()[0] + 1
|
||||||
|
self.cursor.execute('INSERT INTO genre(idGenre, strGenre) VALUES(?, ?)',
|
||||||
|
(genreid, genre))
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO album_genre(
|
||||||
|
idGenre,
|
||||||
|
idAlbum)
|
||||||
|
VALUES (?, ?)
|
||||||
|
''', (genreid, kodiid))
|
||||||
|
elif mediatype == "song":
|
||||||
|
# Delete current genres for clean slate
|
||||||
|
self.cursor.execute('DELETE FROM song_genre WHERE idSong = ?',
|
||||||
|
(kodiid, ))
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute('SELECT idGenre FROM genre WHERE strGenre = ?',
|
||||||
|
(genre, ))
|
||||||
|
try:
|
||||||
|
genreid = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Create the genre
|
||||||
|
self.cursor.execute('SELECT COALESCE(MAX(idGenre),0) FROM genre')
|
||||||
|
genreid = self.cursor.fetchone()[0] + 1
|
||||||
|
self.cursor.execute('INSERT INTO genre(idGenre, strGenre) values(?, ?)',
|
||||||
|
(genreid, genre))
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO song_genre(
|
||||||
|
idGenre,
|
||||||
|
idSong)
|
||||||
|
VALUES (?, ?)
|
||||||
|
''', (genreid, kodiid))
|
||||||
|
|
||||||
|
def add_artist(self, name, musicbrainz):
|
||||||
|
"""
|
||||||
|
Adds a single artist's name to the db
|
||||||
|
"""
|
||||||
|
self.cursor.execute('''
|
||||||
|
SELECT idArtist, strArtist
|
||||||
|
FROM artist
|
||||||
|
WHERE strMusicBrainzArtistID = ?
|
||||||
|
''', (musicbrainz, ))
|
||||||
|
try:
|
||||||
|
result = self.cursor.fetchone()
|
||||||
|
artistid = result[0]
|
||||||
|
artistname = result[1]
|
||||||
|
except TypeError:
|
||||||
|
self.cursor.execute('SELECT idArtist FROM artist WHERE strArtist = ? COLLATE NOCASE',
|
||||||
|
(name, ))
|
||||||
|
try:
|
||||||
|
artistid = self.cursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# Krypton has a dummy first entry idArtist: 1 strArtist:
|
||||||
|
# [Missing Tag] strMusicBrainzArtistID: Artist Tag Missing
|
||||||
|
self.cursor.execute('SELECT COALESCE(MAX(idArtist),1) FROM artist')
|
||||||
|
artistid = self.cursor.fetchone()[0] + 1
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT INTO artist(
|
||||||
|
idArtist,
|
||||||
|
strArtist,
|
||||||
|
strMusicBrainzArtistID)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', (artistid, name, musicbrainz))
|
||||||
|
else:
|
||||||
|
if artistname != name:
|
||||||
|
self.cursor.execute('UPDATE artist SET strArtist = ? WHERE idArtist = ?',
|
||||||
|
(name, artistid,))
|
||||||
|
return artistid
|
17
resources/lib/kodi_db/texture.py
Normal file
17
resources/lib/kodi_db/texture.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
|
||||||
|
|
||||||
|
class KodiTextureDB(common.KodiDBBase):
|
||||||
|
db_kind = 'texture'
|
||||||
|
|
||||||
|
def url_not_yet_cached(self, url):
|
||||||
|
"""
|
||||||
|
Returns True if url has not yet been cached to the Kodi texture cache
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT url FROM texture WHERE url = ? LIMIT 1',
|
||||||
|
(url, ))
|
||||||
|
return self.cursor.fetchone() is None
|
|
@ -1,52 +1,17 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
|
||||||
Connect to the Kodi databases (video and music) and operate on them
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from sqlite3 import IntegrityError
|
from sqlite3 import IntegrityError
|
||||||
|
|
||||||
from . import artwork, utils, variables as v, state, path_ops
|
from . import common
|
||||||
|
from .. import path_ops, utils, variables as v, state
|
||||||
|
|
||||||
###############################################################################
|
LOG = getLogger('PLEX.kodi_db.video')
|
||||||
|
|
||||||
LOG = getLogger('PLEX.kodidb_functions')
|
|
||||||
|
|
||||||
|
|
||||||
class GetKodiDB(object):
|
class KodiVideoDB(common.KodiDBBase):
|
||||||
"""
|
db_kind = 'video'
|
||||||
Usage: with GetKodiDB(db_type) as kodi_db:
|
|
||||||
do stuff with kodi_db
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
db_type: DB to open: 'video', 'music', 'plex', 'texture'
|
|
||||||
|
|
||||||
On exiting "with" (no matter what), commits get automatically committed
|
|
||||||
and the db gets closed
|
|
||||||
"""
|
|
||||||
def __init__(self, db_type):
|
|
||||||
self.kodiconn = None
|
|
||||||
self.db_type = db_type
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.kodiconn = utils.kodi_sql(self.db_type)
|
|
||||||
kodi_db = KodiDBMethods(self.kodiconn.cursor())
|
|
||||||
return kodi_db
|
|
||||||
|
|
||||||
def __exit__(self, typus, value, traceback):
|
|
||||||
self.kodiconn.commit()
|
|
||||||
self.kodiconn.close()
|
|
||||||
|
|
||||||
|
|
||||||
class KodiDBMethods(object):
|
|
||||||
"""
|
|
||||||
Best used indirectly with another Class GetKodiDB:
|
|
||||||
with GetKodiDB(db_type) as kodi_db:
|
|
||||||
kodi_db.method()
|
|
||||||
"""
|
|
||||||
def __init__(self, cursor):
|
|
||||||
self.cursor = cursor
|
|
||||||
|
|
||||||
def setup_path_table(self):
|
def setup_path_table(self):
|
||||||
"""
|
"""
|
||||||
|
@ -155,28 +120,6 @@ class KodiDBMethods(object):
|
||||||
content, scraper, 1))
|
content, scraper, 1))
|
||||||
return pathid
|
return pathid
|
||||||
|
|
||||||
def add_music_path(self, path, hash_string=None):
|
|
||||||
"""
|
|
||||||
Add the path (unicode) to the music DB, if it does not exist already.
|
|
||||||
Returns the path id
|
|
||||||
Set hash_string to something unicode to set the strHash attribute
|
|
||||||
"""
|
|
||||||
# SQL won't return existing paths otherwise
|
|
||||||
path = '' if path is None else path
|
|
||||||
self.cursor.execute('SELECT idPath FROM path WHERE strPath = ?',
|
|
||||||
(path,))
|
|
||||||
try:
|
|
||||||
pathid = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
self.cursor.execute("SELECT COALESCE(MAX(idPath),0) FROM path")
|
|
||||||
pathid = self.cursor.fetchone()[0] + 1
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT INTO path(idPath, strPath, strHash)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
''',
|
|
||||||
(pathid, path, hash_string))
|
|
||||||
return pathid
|
|
||||||
|
|
||||||
def get_path(self, path):
|
def get_path(self, path):
|
||||||
"""
|
"""
|
||||||
Returns the idPath from the path table for path [unicode] or None
|
Returns the idPath from the path table for path [unicode] or None
|
||||||
|
@ -443,7 +386,7 @@ class KodiDBMethods(object):
|
||||||
self.cursor.execute(query_actor_delete, (person[0],))
|
self.cursor.execute(query_actor_delete, (person[0],))
|
||||||
if kind == 'actor':
|
if kind == 'actor':
|
||||||
# Delete any associated artwork
|
# Delete any associated artwork
|
||||||
artwork.delete_artwork(person[0], 'actor', self.cursor)
|
self.delete_artwork(person[0], 'actor')
|
||||||
# Save new people to Kodi DB by iterating over the remaining entries
|
# Save new people to Kodi DB by iterating over the remaining entries
|
||||||
if kind == 'actor':
|
if kind == 'actor':
|
||||||
query = 'INSERT INTO actor_link VALUES (?, ?, ?, ?, ?)'
|
query = 'INSERT INTO actor_link VALUES (?, ?, ?, ?, ?)'
|
||||||
|
@ -489,11 +432,10 @@ class KodiDBMethods(object):
|
||||||
'VALUES (?, ?)',
|
'VALUES (?, ?)',
|
||||||
(actor_id, name))
|
(actor_id, name))
|
||||||
if art_url:
|
if art_url:
|
||||||
artwork.modify_art(art_url,
|
self.modify_art(art_url,
|
||||||
actor_id,
|
actor_id,
|
||||||
'actor',
|
'actor',
|
||||||
'thumb',
|
'thumb')
|
||||||
self.cursor)
|
|
||||||
return actor_id
|
return actor_id
|
||||||
|
|
||||||
def get_art(self, kodi_id, kodi_type):
|
def get_art(self, kodi_id, kodi_type):
|
||||||
|
@ -610,26 +552,6 @@ class KodiDBMethods(object):
|
||||||
return
|
return
|
||||||
return movie_id, typus
|
return movie_id, typus
|
||||||
|
|
||||||
def music_id_from_filename(self, filename, path):
|
|
||||||
"""
|
|
||||||
Returns the Kodi song_id from the Kodi music database or None if not
|
|
||||||
found OR something went wrong.
|
|
||||||
"""
|
|
||||||
self.cursor.execute('SELECT idPath FROM path WHERE strPath = ?',
|
|
||||||
(path,))
|
|
||||||
path_ids = self.cursor.fetchall()
|
|
||||||
if len(path_ids) != 1:
|
|
||||||
LOG.debug('Found wrong number of path ids: %s for path %s, abort',
|
|
||||||
path_ids, path)
|
|
||||||
return
|
|
||||||
self.cursor.execute('SELECT idSong FROM song WHERE strFileName = ? AND idPath = ?',
|
|
||||||
(filename, path_ids[0][0]))
|
|
||||||
song_ids = self.cursor.fetchall()
|
|
||||||
if len(song_ids) != 1:
|
|
||||||
LOG.info('Found wrong number of songs %s, abort', song_ids)
|
|
||||||
return
|
|
||||||
return song_ids[0][0]
|
|
||||||
|
|
||||||
def get_resume(self, file_id):
|
def get_resume(self, file_id):
|
||||||
"""
|
"""
|
||||||
Returns the first resume point in seconds (int) if found, else None for
|
Returns the first resume point in seconds (int) if found, else None for
|
||||||
|
@ -803,208 +725,6 @@ class KodiDBMethods(object):
|
||||||
''', (seasonid, showid, seasonnumber))
|
''', (seasonid, showid, seasonnumber))
|
||||||
return seasonid
|
return seasonid
|
||||||
|
|
||||||
def add_artist(self, name, musicbrainz):
|
|
||||||
"""
|
|
||||||
Adds a single artist's name to the db
|
|
||||||
"""
|
|
||||||
self.cursor.execute('''
|
|
||||||
SELECT idArtist, strArtist
|
|
||||||
FROM artist
|
|
||||||
WHERE strMusicBrainzArtistID = ?
|
|
||||||
''', (musicbrainz, ))
|
|
||||||
try:
|
|
||||||
result = self.cursor.fetchone()
|
|
||||||
artistid = result[0]
|
|
||||||
artistname = result[1]
|
|
||||||
except TypeError:
|
|
||||||
self.cursor.execute('SELECT idArtist FROM artist WHERE strArtist = ? COLLATE NOCASE',
|
|
||||||
(name, ))
|
|
||||||
try:
|
|
||||||
artistid = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Krypton has a dummy first entry idArtist: 1 strArtist:
|
|
||||||
# [Missing Tag] strMusicBrainzArtistID: Artist Tag Missing
|
|
||||||
self.cursor.execute('SELECT COALESCE(MAX(idArtist),1) FROM artist')
|
|
||||||
artistid = self.cursor.fetchone()[0] + 1
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT INTO artist(
|
|
||||||
idArtist,
|
|
||||||
strArtist,
|
|
||||||
strMusicBrainzArtistID)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
''', (artistid, name, musicbrainz))
|
|
||||||
else:
|
|
||||||
if artistname != name:
|
|
||||||
self.cursor.execute('UPDATE artist SET strArtist = ? WHERE idArtist = ?',
|
|
||||||
(name, artistid,))
|
|
||||||
return artistid
|
|
||||||
|
|
||||||
def delete_song_from_song_artist(self, song_id):
|
|
||||||
"""
|
|
||||||
Deletes son from song_artist table and possibly orphaned roles
|
|
||||||
"""
|
|
||||||
self.cursor.execute('SELECT idArtist, idRole FROM song_artist WHERE idSong = ? LIMIT 1',
|
|
||||||
(song_id, ))
|
|
||||||
artist = self.cursor.fetchone()
|
|
||||||
if artist is None:
|
|
||||||
# No entry to begin with
|
|
||||||
return
|
|
||||||
# Delete the entry
|
|
||||||
self.cursor.execute('DELETE FROM song_artist WHERE idSong = ?',
|
|
||||||
(song_id, ))
|
|
||||||
# Check whether we need to delete orphaned roles
|
|
||||||
self.cursor.execute('SELECT idRole FROM song_artist WHERE idRole = ? LIMIT 1',
|
|
||||||
(artist[1], ))
|
|
||||||
if not self.cursor.fetchone():
|
|
||||||
# Delete orphaned role
|
|
||||||
self.cursor.execute('DELETE FROM role WHERE idRole = ?',
|
|
||||||
(artist[1], ))
|
|
||||||
|
|
||||||
def delete_album_from_discography(self, album_id):
|
|
||||||
"""
|
|
||||||
Removes the album with id album_id from the table discography
|
|
||||||
"""
|
|
||||||
# Need to get the album name as a string first!
|
|
||||||
self.cursor.execute('SELECT strAlbum, iYear FROM album WHERE idAlbum = ? LIMIT 1',
|
|
||||||
(album_id, ))
|
|
||||||
try:
|
|
||||||
name, year = self.cursor.fetchone()
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
self.cursor.execute('SELECT idArtist FROM album_artist WHERE idAlbum = ? LIMIT 1',
|
|
||||||
(album_id, ))
|
|
||||||
artist = self.cursor.fetchone()
|
|
||||||
if not artist:
|
|
||||||
return
|
|
||||||
self.cursor.execute('DELETE FROM discography WHERE idArtist = ? AND strAlbum = ? AND strYear = ?',
|
|
||||||
(artist[0], name, year))
|
|
||||||
|
|
||||||
def delete_song_from_song_genre(self, song_id):
|
|
||||||
"""
|
|
||||||
Deletes the one entry with id song_id from the song_genre table.
|
|
||||||
Will also delete orphaned genres from genre table
|
|
||||||
"""
|
|
||||||
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idSong = ?',
|
|
||||||
(song_id, ))
|
|
||||||
genres = self.cursor.fetchall()
|
|
||||||
self.cursor.execute('DELETE FROM song_genre WHERE idSong = ?',
|
|
||||||
(song_id, ))
|
|
||||||
# Check for orphaned genres in both song_genre and album_genre tables
|
|
||||||
for genre in genres:
|
|
||||||
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1',
|
|
||||||
(genre[0], ))
|
|
||||||
if not self.cursor.fetchone():
|
|
||||||
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1',
|
|
||||||
(genre[0], ))
|
|
||||||
if not self.cursor.fetchone():
|
|
||||||
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
|
||||||
(genre[0], ))
|
|
||||||
|
|
||||||
def delete_album_from_album_genre(self, album_id):
|
|
||||||
"""
|
|
||||||
Deletes the one entry with id album_id from the album_genre table.
|
|
||||||
Will also delete orphaned genres from genre table
|
|
||||||
"""
|
|
||||||
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idAlbum = ?',
|
|
||||||
(album_id, ))
|
|
||||||
genres = self.cursor.fetchall()
|
|
||||||
self.cursor.execute('DELETE FROM album_genre WHERE idAlbum = ?',
|
|
||||||
(album_id, ))
|
|
||||||
# Check for orphaned genres in both album_genre and song_genre tables
|
|
||||||
for genre in genres:
|
|
||||||
self.cursor.execute('SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1',
|
|
||||||
(genre[0], ))
|
|
||||||
if not self.cursor.fetchone():
|
|
||||||
self.cursor.execute('SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1',
|
|
||||||
(genre[0], ))
|
|
||||||
if not self.cursor.fetchone():
|
|
||||||
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
|
||||||
(genre[0], ))
|
|
||||||
|
|
||||||
def add_album(self, name, musicbrainz):
|
|
||||||
"""
|
|
||||||
Adds a single album to the DB
|
|
||||||
"""
|
|
||||||
self.cursor.execute('SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?',
|
|
||||||
(musicbrainz, ))
|
|
||||||
try:
|
|
||||||
albumid = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Create the album
|
|
||||||
self.cursor.execute('SELECT COALESCE(MAX(idAlbum),0) FROM album')
|
|
||||||
albumid = self.cursor.fetchone()[0] + 1
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT INTO album(
|
|
||||||
idAlbum,
|
|
||||||
strAlbum,
|
|
||||||
strMusicBrainzAlbumID,
|
|
||||||
strReleaseType)
|
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
''', (albumid, name, musicbrainz, 'album'))
|
|
||||||
return albumid
|
|
||||||
|
|
||||||
def add_music_genres(self, kodiid, genres, mediatype):
|
|
||||||
"""
|
|
||||||
Adds a list of genres (list of unicode) for a certain Kodi item
|
|
||||||
"""
|
|
||||||
if mediatype == "album":
|
|
||||||
# Delete current genres for clean slate
|
|
||||||
self.cursor.execute('DELETE FROM album_genre WHERE idAlbum = ?',
|
|
||||||
(kodiid, ))
|
|
||||||
for genre in genres:
|
|
||||||
self.cursor.execute('SELECT idGenre FROM genre WHERE strGenre = ?',
|
|
||||||
(genre, ))
|
|
||||||
try:
|
|
||||||
genreid = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Create the genre
|
|
||||||
self.cursor.execute('SELECT COALESCE(MAX(idGenre),0) FROM genre')
|
|
||||||
genreid = self.cursor.fetchone()[0] + 1
|
|
||||||
self.cursor.execute('INSERT INTO genre(idGenre, strGenre) VALUES(?, ?)',
|
|
||||||
(genreid, genre))
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT OR REPLACE INTO album_genre(
|
|
||||||
idGenre,
|
|
||||||
idAlbum)
|
|
||||||
VALUES (?, ?)
|
|
||||||
''', (genreid, kodiid))
|
|
||||||
elif mediatype == "song":
|
|
||||||
# Delete current genres for clean slate
|
|
||||||
self.cursor.execute('DELETE FROM song_genre WHERE idSong = ?',
|
|
||||||
(kodiid, ))
|
|
||||||
for genre in genres:
|
|
||||||
self.cursor.execute('SELECT idGenre FROM genre WHERE strGenre = ?',
|
|
||||||
(genre, ))
|
|
||||||
try:
|
|
||||||
genreid = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Create the genre
|
|
||||||
self.cursor.execute('SELECT COALESCE(MAX(idGenre),0) FROM genre')
|
|
||||||
genreid = self.cursor.fetchone()[0] + 1
|
|
||||||
self.cursor.execute('INSERT INTO genre(idGenre, strGenre) values(?, ?)',
|
|
||||||
(genreid, genre))
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT OR REPLACE INTO song_genre(
|
|
||||||
idGenre,
|
|
||||||
idSong)
|
|
||||||
VALUES (?, ?)
|
|
||||||
''', (genreid, kodiid))
|
|
||||||
|
|
||||||
# Krypton only stuff ##############################
|
|
||||||
|
|
||||||
def update_userrating(self, kodi_id, kodi_type, userrating):
|
|
||||||
"""
|
|
||||||
Updates userrating for >=Krypton
|
|
||||||
"""
|
|
||||||
if kodi_type == v.KODI_TYPE_MOVIE:
|
|
||||||
identifier = 'idMovie'
|
|
||||||
elif kodi_type == v.KODI_TYPE_EPISODE:
|
|
||||||
identifier = 'idEpisode'
|
|
||||||
elif kodi_type == v.KODI_TYPE_SONG:
|
|
||||||
identifier = 'idSong'
|
|
||||||
self.cursor.execute('UPDATE %s SET userrating = ? WHERE ? = ?' % kodi_type,
|
|
||||||
(userrating, identifier, kodi_id))
|
|
||||||
|
|
||||||
def add_uniqueid(self, *args):
|
def add_uniqueid(self, *args):
|
||||||
"""
|
"""
|
||||||
Feed with:
|
Feed with:
|
||||||
|
@ -1103,141 +823,151 @@ class KodiDBMethods(object):
|
||||||
self.cursor.execute('DELETE FROM rating WHERE media_id = ? AND media_type = ?',
|
self.cursor.execute('DELETE FROM rating WHERE media_id = ? AND media_type = ?',
|
||||||
(kodi_id, kodi_type))
|
(kodi_id, kodi_type))
|
||||||
|
|
||||||
def art_urls(self, kodi_id, kodi_type):
|
def new_show_id(self):
|
||||||
return (x[0] for x in
|
self.cursor.execute('SELECT COALESCE(MAX(idShow), 0) FROM tvshow')
|
||||||
self.cursor.execute('SELECT url FROM art WHERE media_id = ? AND media_type = ?',
|
return self.cursor.fetchone()[0] + 1
|
||||||
(kodi_id, kodi_type)))
|
|
||||||
|
|
||||||
def artwork_generator(self, kodi_type):
|
def new_episode_id(self):
|
||||||
"""
|
self.cursor.execute('SELECT COALESCE(MAX(idEpisode), 0) FROM episode')
|
||||||
"""
|
return self.cursor.fetchone()[0] + 1
|
||||||
return (x[0] for x in
|
|
||||||
self.cursor.execute('SELECT url FROM art WHERE type == ?',
|
|
||||||
(kodi_type, )))
|
|
||||||
|
|
||||||
def url_not_yet_cached(self, url):
|
def add_episode(self, *args):
|
||||||
"""
|
self.cursor.execute(
|
||||||
Returns True if url has not yet been cached to the Kodi texture cache
|
'''
|
||||||
"""
|
INSERT INTO episode(
|
||||||
self.cursor.execute('SELECT url FROM texture WHERE url == ? LIMIT 1',
|
idEpisode,
|
||||||
(url, ))
|
idFile,
|
||||||
return self.cursor.fetchone() is None
|
c00,
|
||||||
|
c01,
|
||||||
|
c03,
|
||||||
|
c04,
|
||||||
|
c05,
|
||||||
|
c09,
|
||||||
|
c10,
|
||||||
|
c12,
|
||||||
|
c13,
|
||||||
|
c14,
|
||||||
|
idShow,
|
||||||
|
c15,
|
||||||
|
c16,
|
||||||
|
c18,
|
||||||
|
c19,
|
||||||
|
idSeason,
|
||||||
|
userrating)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
''', (args))
|
||||||
|
|
||||||
|
def update_episode(self, *args):
|
||||||
|
self.cursor.execute(
|
||||||
|
'''
|
||||||
|
UPDATE episode
|
||||||
|
SET c00 = ?,
|
||||||
|
c01 = ?,
|
||||||
|
c03 = ?,
|
||||||
|
c04 = ?,
|
||||||
|
c05 = ?,
|
||||||
|
c09 = ?,
|
||||||
|
c10 = ?,
|
||||||
|
c12 = ?,
|
||||||
|
c13 = ?,
|
||||||
|
c14 = ?,
|
||||||
|
c15 = ?,
|
||||||
|
c16 = ?,
|
||||||
|
c18 = ?,
|
||||||
|
c19 = ?,
|
||||||
|
idFile=?,
|
||||||
|
idSeason = ?,
|
||||||
|
userrating = ?
|
||||||
|
WHERE idEpisode = ?
|
||||||
|
''', (args))
|
||||||
|
|
||||||
def kodiid_from_filename(path, kodi_type=None, db_type=None):
|
def add_show(self, *args):
|
||||||
"""
|
self.cursor.execute(
|
||||||
Returns kodi_id if we have an item in the Kodi video or audio database with
|
'''
|
||||||
said path. Feed with either koditype, e.v. 'movie', 'song' or the DB
|
INSERT INTO tvshow(
|
||||||
you want to poll ('video' or 'music')
|
idShow,
|
||||||
Returns None, <kodi_type> if not possible
|
c00,
|
||||||
"""
|
c01,
|
||||||
kodi_id = None
|
c04,
|
||||||
path = utils.try_decode(path)
|
c05,
|
||||||
try:
|
c08,
|
||||||
filename = path.rsplit('/', 1)[1]
|
c09,
|
||||||
path = path.rsplit('/', 1)[0] + '/'
|
c12,
|
||||||
except IndexError:
|
c13,
|
||||||
filename = path.rsplit('\\', 1)[1]
|
c14,
|
||||||
path = path.rsplit('\\', 1)[0] + '\\'
|
c15)
|
||||||
if kodi_type == v.KODI_TYPE_SONG or db_type == 'music':
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
with GetKodiDB('music') as kodi_db:
|
''', (args))
|
||||||
try:
|
|
||||||
kodi_id = kodi_db.music_id_from_filename(filename, path)
|
|
||||||
except TypeError:
|
|
||||||
LOG.debug('No Kodi audio db element found for path %s', path)
|
|
||||||
else:
|
|
||||||
kodi_type = v.KODI_TYPE_SONG
|
|
||||||
else:
|
|
||||||
with GetKodiDB('video') as kodi_db:
|
|
||||||
try:
|
|
||||||
kodi_id, kodi_type = kodi_db.video_id_from_filename(filename,
|
|
||||||
path)
|
|
||||||
except TypeError:
|
|
||||||
LOG.debug('No kodi video db element found for path %s', path)
|
|
||||||
return kodi_id, kodi_type
|
|
||||||
|
|
||||||
|
def update_show(self, *args):
|
||||||
|
self.cursor.execute(
|
||||||
|
'''
|
||||||
|
UPDATE tvshow
|
||||||
|
SET c00 = ?,
|
||||||
|
c01 = ?,
|
||||||
|
c04 = ?,
|
||||||
|
c05 = ?,
|
||||||
|
c08 = ?,
|
||||||
|
c09 = ?,
|
||||||
|
c12 = ?,
|
||||||
|
c13 = ?,
|
||||||
|
c14 = ?,
|
||||||
|
c15 = ?
|
||||||
|
WHERE idShow = ?
|
||||||
|
''', (args))
|
||||||
|
|
||||||
def setup_kodi_default_entries():
|
def add_showlinkpath(self, kodi_id, kodi_pathid):
|
||||||
"""
|
self.cursor.execute('INSERT INTO tvshowlinkpath(idShow, idPath) VALUES (?, ?)',
|
||||||
Makes sure that we retain the Kodi standard databases. E.g. that there
|
(kodi_id, kodi_pathid))
|
||||||
is a dummy artist with ID 1
|
|
||||||
"""
|
|
||||||
if utils.settings('enableMusic') == 'true':
|
|
||||||
with GetKodiDB('music') as kodi_db:
|
|
||||||
kodi_db.cursor.execute('''
|
|
||||||
INSERT OR REPLACE INTO artist(
|
|
||||||
idArtist,
|
|
||||||
strArtist,
|
|
||||||
strMusicBrainzArtistID)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
''', (1, '[Missing Tag]', 'Artist Tag Missing'))
|
|
||||||
if v.KODIVERSION >= 18:
|
|
||||||
kodi_db.cursor.execute('''
|
|
||||||
INSERT OR REPLACE INTO versiontagscan(
|
|
||||||
idVersion,
|
|
||||||
iNeedsScan,
|
|
||||||
lastscanned)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
''', (v.DB_MUSIC_VERSION[v.KODIVERSION],
|
|
||||||
0,
|
|
||||||
utils.unix_date_to_kodi(utils.unix_timestamp())))
|
|
||||||
|
|
||||||
|
def remove_show(self, kodi_id):
|
||||||
|
self.cursor.execute('DELETE FROM tvshow WHERE idShow = ?', (kodi_id,))
|
||||||
|
|
||||||
def reset_cached_images():
|
def remove_season(self, kodi_id):
|
||||||
LOG.info('Resetting cached artwork')
|
self.cursor.execute('DELETE FROM seasons WHERE idSeason = ?',
|
||||||
# Remove all existing textures first
|
(kodi_id,))
|
||||||
path = path_ops.translate_path('special://thumbnails/')
|
|
||||||
if path_ops.exists(path):
|
|
||||||
path_ops.rmtree(path, ignore_errors=True)
|
|
||||||
paths = ('', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
||||||
'a', 'b', 'c', 'd', 'e', 'f',
|
|
||||||
'Video', 'plex')
|
|
||||||
for path in paths:
|
|
||||||
new_path = path_ops.translate_path('special://thumbnails/%s' % path)
|
|
||||||
path_ops.makedirs(path_ops.encode_path(new_path))
|
|
||||||
with GetKodiDB('texture') as kodi_db:
|
|
||||||
kodi_db.cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type=?',
|
|
||||||
('table', ))
|
|
||||||
rows = kodi_db.cursor.fetchall()
|
|
||||||
for row in rows:
|
|
||||||
if row[0] != 'version':
|
|
||||||
kodi_db.cursor.execute("DELETE FROM %s" % row[0])
|
|
||||||
|
|
||||||
|
def remove_episode(self, kodi_id):
|
||||||
|
self.cursor.execute('DELETE FROM episode WHERE idEpisode = ?',
|
||||||
|
(kodi_id,))
|
||||||
|
|
||||||
def wipe_dbs(music=True):
|
def new_movie_id(self):
|
||||||
"""
|
self.cursor.execute('SELECT COALESCE(MAX(idMovie), 0) FROM movie')
|
||||||
Completely resets the Kodi databases 'video', 'texture' and 'music' (if
|
return self.cursor.fetchone()[0] + 1
|
||||||
music sync is enabled)
|
|
||||||
"""
|
|
||||||
LOG.warn('Wiping Kodi databases!')
|
|
||||||
kinds = ['video', 'texture']
|
|
||||||
if music:
|
|
||||||
kinds.append('music')
|
|
||||||
for db in kinds:
|
|
||||||
with GetKodiDB(db) as kodi_db:
|
|
||||||
kodi_db.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
||||||
tables = kodi_db.cursor.fetchall()
|
|
||||||
tables = [i[0] for i in tables]
|
|
||||||
if 'version' in tables:
|
|
||||||
tables.remove('version')
|
|
||||||
if 'versiontagscan' in tables:
|
|
||||||
tables.remove('versiontagscan')
|
|
||||||
for table in tables:
|
|
||||||
kodi_db.cursor.execute('DELETE FROM %s' % table)
|
|
||||||
setup_kodi_default_entries()
|
|
||||||
# Make sure Kodi knows we wiped the databases
|
|
||||||
import xbmc
|
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
|
||||||
if utils.settings('enableMusic') == 'true':
|
|
||||||
xbmc.executebuiltin('UpdateLibrary(music)')
|
|
||||||
|
|
||||||
|
def add_movie(self, *args):
|
||||||
|
self.cursor.execute(
|
||||||
|
'''
|
||||||
|
INSERT OR REPLACE INTO movie(
|
||||||
|
idMovie,
|
||||||
|
idFile,
|
||||||
|
c00,
|
||||||
|
c01,
|
||||||
|
c02,
|
||||||
|
c03,
|
||||||
|
c04,
|
||||||
|
c05,
|
||||||
|
c06,
|
||||||
|
c07,
|
||||||
|
c09,
|
||||||
|
c10,
|
||||||
|
c11,
|
||||||
|
c12,
|
||||||
|
c14,
|
||||||
|
c15,
|
||||||
|
c16,
|
||||||
|
c18,
|
||||||
|
c19,
|
||||||
|
c21,
|
||||||
|
c22,
|
||||||
|
c23,
|
||||||
|
premiered,
|
||||||
|
userrating)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?)
|
||||||
|
''', (args))
|
||||||
|
|
||||||
KODIDB_FROM_PLEXTYPE = {
|
def remove_movie(self, kodi_id):
|
||||||
v.PLEX_TYPE_MOVIE: GetKodiDB('video'),
|
self.cursor.execute('DELETE FROM movie WHERE idMovie = ?', (kodi_id,))
|
||||||
v.PLEX_TYPE_SHOW: GetKodiDB('video'),
|
|
||||||
v.PLEX_TYPE_SEASON: GetKodiDB('video'),
|
|
||||||
v.PLEX_TYPE_EPISODE: GetKodiDB('video'),
|
|
||||||
v.PLEX_TYPE_ARTIST: GetKodiDB('music'),
|
|
||||||
v.PLEX_TYPE_ALBUM: GetKodiDB('music'),
|
|
||||||
v.PLEX_TYPE_SONG: GetKodiDB('music')
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ import xbmc
|
||||||
from xbmcgui import Window
|
from xbmcgui import Window
|
||||||
|
|
||||||
from .plex_db import PlexDB
|
from .plex_db import PlexDB
|
||||||
from . import kodidb_functions as kodidb
|
from . import kodi_db
|
||||||
from . import utils
|
from . import utils
|
||||||
from . import plex_functions as PF
|
from . import plex_functions as PF
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
|
@ -302,7 +302,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
plex_type = None
|
plex_type = None
|
||||||
# If using direct paths and starting playback from a widget
|
# If using direct paths and starting playback from a widget
|
||||||
if not kodi_id and kodi_type and path:
|
if not kodi_id and kodi_type and path:
|
||||||
kodi_id, _ = kodidb.kodiid_from_filename(path, kodi_type)
|
kodi_id, _ = kodi_db.kodiid_from_filename(path, kodi_type)
|
||||||
if kodi_id:
|
if kodi_id:
|
||||||
with PlexDB() as plexdb:
|
with PlexDB() as plexdb:
|
||||||
db_item = plexdb.item_by_kodi_id(kodi_id, kodi_type)
|
db_item = plexdb.item_by_kodi_id(kodi_id, kodi_type)
|
||||||
|
@ -559,8 +559,8 @@ def _record_playstate(status, ended):
|
||||||
last_played = utils.unix_date_to_kodi(utils.unix_timestamp())
|
last_played = utils.unix_date_to_kodi(utils.unix_timestamp())
|
||||||
if playcount is None:
|
if playcount is None:
|
||||||
LOG.debug('playcount not found, looking it up in the Kodi DB')
|
LOG.debug('playcount not found, looking it up in the Kodi DB')
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodi_db.KodiVideoDB() as kodidb:
|
||||||
playcount = kodi_db.get_playcount(db_item['kodi_fileid'])
|
playcount = kodidb.get_playcount(db_item['kodi_fileid'])
|
||||||
playcount = 0 if playcount is None else playcount
|
playcount = 0 if playcount is None else playcount
|
||||||
if time < v.IGNORE_SECONDS_AT_START:
|
if time < v.IGNORE_SECONDS_AT_START:
|
||||||
LOG.debug('Ignoring playback less than %s seconds',
|
LOG.debug('Ignoring playback less than %s seconds',
|
||||||
|
@ -574,8 +574,8 @@ def _record_playstate(status, ended):
|
||||||
v.MARK_PLAYED_AT)
|
v.MARK_PLAYED_AT)
|
||||||
playcount += 1
|
playcount += 1
|
||||||
time = 0
|
time = 0
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodi_db.KodiVideoDB() as kodidb:
|
||||||
kodi_db.set_resume(db_item['kodi_fileid'],
|
kodidb.set_resume(db_item['kodi_fileid'],
|
||||||
time,
|
time,
|
||||||
totaltime,
|
totaltime,
|
||||||
playcount,
|
playcount,
|
||||||
|
@ -600,9 +600,9 @@ def _clean_file_table():
|
||||||
"""
|
"""
|
||||||
LOG.debug('Start cleaning Kodi files table')
|
LOG.debug('Start cleaning Kodi files table')
|
||||||
xbmc.sleep(2000)
|
xbmc.sleep(2000)
|
||||||
with kodidb.GetKodiDB('video') as kodi_db_1:
|
with kodi_db.KodiVideoDB() as kodidb_1:
|
||||||
with kodidb.GetKodiDB('video') as kodi_db_2:
|
with kodi_db.KodiVideoDB() as kodidb_2:
|
||||||
for file_id in kodi_db_1.obsolete_file_ids():
|
for file_id in kodidb_1.obsolete_file_ids():
|
||||||
LOG.debug('Removing obsolete Kodi file_id %s', file_id)
|
LOG.debug('Removing obsolete Kodi file_id %s', file_id)
|
||||||
kodi_db_2.remove_file(file_id, remove_orphans=False)
|
kodidb_2.remove_file(file_id, remove_orphans=False)
|
||||||
LOG.debug('Done cleaning up Kodi file table')
|
LOG.debug('Done cleaning up Kodi file table')
|
||||||
|
|
|
@ -6,7 +6,8 @@ import xbmc
|
||||||
from . import common
|
from . import common
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import backgroundthread, utils, kodidb_functions as kodidb
|
from ..kodi_db import KodiVideoDB
|
||||||
|
from .. import backgroundthread, utils
|
||||||
from .. import itemtypes, plex_functions as PF, variables as v, state
|
from .. import itemtypes, plex_functions as PF, variables as v, state
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,8 +88,8 @@ def process_fanart(plex_id, plex_type, refresh=False):
|
||||||
LOG.error('Could not get Kodi id for plex id %s', plex_id)
|
LOG.error('Could not get Kodi id for plex id %s', plex_id)
|
||||||
return
|
return
|
||||||
if not refresh:
|
if not refresh:
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
artworks = kodi_db.get_art(db_item['kodi_id'],
|
artworks = kodidb.get_art(db_item['kodi_id'],
|
||||||
db_item['kodi_type'])
|
db_item['kodi_type'])
|
||||||
# Check if we even need to get additional art
|
# Check if we even need to get additional art
|
||||||
for key in v.ALL_KODI_ARTWORK:
|
for key in v.ALL_KODI_ARTWORK:
|
||||||
|
@ -117,8 +118,8 @@ def process_fanart(plex_id, plex_type, refresh=False):
|
||||||
if plex_type == v.PLEX_TYPE_MOVIE:
|
if plex_type == v.PLEX_TYPE_MOVIE:
|
||||||
for _, setname in api.collection_list():
|
for _, setname in api.collection_list():
|
||||||
LOG.debug('Getting artwork for movie set %s', setname)
|
LOG.debug('Getting artwork for movie set %s', setname)
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
setid = kodi_db.create_collection(setname)
|
setid = kodidb.create_collection(setname)
|
||||||
external_set_artwork = api.set_artwork()
|
external_set_artwork = api.set_artwork()
|
||||||
if external_set_artwork and PREFER_KODI_COLLECTION_ART:
|
if external_set_artwork and PREFER_KODI_COLLECTION_ART:
|
||||||
kodi_artwork = api.artwork(kodi_id=setid,
|
kodi_artwork = api.artwork(kodi_id=setid,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import copy
|
||||||
from . import common, videonodes
|
from . import common, videonodes
|
||||||
from ..utils import cast
|
from ..utils import cast
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import kodidb_functions as kodidb
|
from .. import kodi_db
|
||||||
from .. import itemtypes
|
from .. import itemtypes
|
||||||
from .. import plex_functions as PF, music, utils, state, variables as v
|
from .. import plex_functions as PF, music, utils, state, variables as v
|
||||||
|
|
||||||
|
@ -58,10 +58,10 @@ def sync_from_pms():
|
||||||
# Backup old sections to delete them later, if needed (at the end
|
# Backup old sections to delete them later, if needed (at the end
|
||||||
# of this method, only unused sections will be left in old_sections)
|
# of this method, only unused sections will be left in old_sections)
|
||||||
old_sections = list(plexdb.section_ids())
|
old_sections = list(plexdb.section_ids())
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodi_db.KodiVideoDB() as kodidb:
|
||||||
for section in sections:
|
for section in sections:
|
||||||
_process_section(section,
|
_process_section(section,
|
||||||
kodi_db,
|
kodidb,
|
||||||
plexdb,
|
plexdb,
|
||||||
sorted_sections,
|
sorted_sections,
|
||||||
old_sections,
|
old_sections,
|
||||||
|
@ -77,7 +77,7 @@ def sync_from_pms():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _process_section(section_xml, kodi_db, plexdb, sorted_sections,
|
def _process_section(section_xml, kodidb, plexdb, sorted_sections,
|
||||||
old_sections, totalnodes):
|
old_sections, totalnodes):
|
||||||
folder = section_xml.attrib
|
folder = section_xml.attrib
|
||||||
plex_type = folder['type']
|
plex_type = folder['type']
|
||||||
|
@ -101,7 +101,7 @@ def _process_section(section_xml, kodi_db, plexdb, sorted_sections,
|
||||||
current_tagid = section[3]
|
current_tagid = section[3]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
LOG.info('Creating section id: %s in Plex database.', section_id)
|
LOG.info('Creating section id: %s in Plex database.', section_id)
|
||||||
tagid = kodi_db.create_tag(section_name)
|
tagid = kodidb.create_tag(section_name)
|
||||||
# Create playlist for the video library
|
# Create playlist for the video library
|
||||||
if (section_name not in playlists and
|
if (section_name not in playlists and
|
||||||
plex_type in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
plex_type in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
|
@ -133,7 +133,7 @@ def _process_section(section_xml, kodi_db, plexdb, sorted_sections,
|
||||||
if current_sectionname != section_name:
|
if current_sectionname != section_name:
|
||||||
LOG.info('section id: %s new sectionname: %s',
|
LOG.info('section id: %s new sectionname: %s',
|
||||||
section_id, section_name)
|
section_id, section_name)
|
||||||
tagid = kodi_db.create_tag(section_name)
|
tagid = kodidb.create_tag(section_name)
|
||||||
|
|
||||||
# Update view with new info
|
# Update view with new info
|
||||||
plexdb.add_section(section_id,
|
plexdb.add_section(section_id,
|
||||||
|
@ -177,7 +177,7 @@ def _process_section(section_xml, kodi_db, plexdb, sorted_sections,
|
||||||
# Update items with new tag
|
# Update items with new tag
|
||||||
for item in plexdb.kodi_id_by_section(section_id):
|
for item in plexdb.kodi_id_by_section(section_id):
|
||||||
# Remove the "s" from viewtype for tags
|
# Remove the "s" from viewtype for tags
|
||||||
kodi_db.update_tag(
|
kodidb.update_tag(
|
||||||
current_tagid, tagid, item[0], current_sectiontype[:-1])
|
current_tagid, tagid, item[0], current_sectiontype[:-1])
|
||||||
else:
|
else:
|
||||||
# Validate the playlist exists or recreate it
|
# Validate the playlist exists or recreate it
|
||||||
|
@ -214,24 +214,25 @@ def delete_sections(old_sections):
|
||||||
with PlexDB() as plexdb:
|
with PlexDB() as plexdb:
|
||||||
old_sections = [plexdb.section(x) for x in old_sections]
|
old_sections = [plexdb.section(x) for x in old_sections]
|
||||||
LOG.info("Removing entire Plex library sections: %s", old_sections)
|
LOG.info("Removing entire Plex library sections: %s", old_sections)
|
||||||
with kodidb.GetKodiDB() as kodi_db:
|
with kodi_db.KodiVideoDB() as kodidb:
|
||||||
for section in old_sections:
|
for section in old_sections:
|
||||||
if section[2] == v.KODI_TYPE_MOVIE:
|
if section[2] == v.KODI_TYPE_PHOTO:
|
||||||
|
# not synced
|
||||||
|
plexdb.remove_section(section[0])
|
||||||
|
elif section[2] == v.KODI_TYPE_MOVIE:
|
||||||
video_library_update = True
|
video_library_update = True
|
||||||
context = itemtypes.Movie(plexdb=plexdb,
|
context = itemtypes.Movie(plexdb=plexdb,
|
||||||
kodi_db=kodi_db)
|
kodidb=kodidb)
|
||||||
elif section[2] == v.KODI_TYPE_SHOW:
|
elif section[2] == v.KODI_TYPE_SHOW:
|
||||||
video_library_update = True
|
video_library_update = True
|
||||||
context = itemtypes.Show(plexdb=plexdb,
|
context = itemtypes.Show(plexdb=plexdb,
|
||||||
kodi_db=kodi_db)
|
kodidb=kodidb)
|
||||||
elif section[2] == v.KODI_TYPE_ARTIST:
|
with kodi_db.KodiMusicDB() as kodidb:
|
||||||
|
for section in old_sections:
|
||||||
|
if section[2] == v.KODI_TYPE_ARTIST:
|
||||||
music_library_update = True
|
music_library_update = True
|
||||||
context = itemtypes.Artist(plexdb=plexdb,
|
context = itemtypes.Artist(plexdb=plexdb,
|
||||||
kodi_db=kodi_db)
|
kodidb=kodidb)
|
||||||
elif section[2] == v.KODI_TYPE_PHOTO:
|
|
||||||
# not synced
|
|
||||||
plexdb.remove_section(section[0])
|
|
||||||
continue
|
|
||||||
for plex_id in plexdb.plexid_by_section(section[0]):
|
for plex_id in plexdb.plexid_by_section(section[0]):
|
||||||
context.remove(plex_id)
|
context.remove(plex_id)
|
||||||
# Only remove Plex entry if we've removed all items first
|
# Only remove Plex entry if we've removed all items first
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .full_sync import PLAYLIST_SYNC_ENABLED
|
||||||
from .fanart import SYNC_FANART, FanartTask
|
from .fanart import SYNC_FANART, FanartTask
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import kodidb_functions as kodidb
|
from .. import kodi_db
|
||||||
from .. import backgroundthread, playlists, plex_functions as PF, itemtypes
|
from .. import backgroundthread, playlists, plex_functions as PF, itemtypes
|
||||||
from .. import artwork, utils, variables as v, state
|
from .. import artwork, utils, variables as v, state
|
||||||
|
|
||||||
|
@ -360,6 +360,6 @@ def cache_artwork(plex_id, plex_type, kodi_id=None, kodi_type=None):
|
||||||
LOG.error('Could not retrieve Plex db info for %s', plex_id)
|
LOG.error('Could not retrieve Plex db info for %s', plex_id)
|
||||||
return
|
return
|
||||||
kodi_id, kodi_type = item['kodi_id'], item['kodi_type']
|
kodi_id, kodi_type = item['kodi_id'], item['kodi_type']
|
||||||
with kodidb.KODIDB_FROM_PLEXTYPE[plex_type] as kodi_db:
|
with kodi_db.KODIDB_FROM_PLEXTYPE[plex_type] as kodidb:
|
||||||
for url in kodi_db.art_urls(kodi_id, kodi_type):
|
for url in kodidb.art_urls(kodi_id, kodi_type):
|
||||||
artwork.cache_url(url)
|
artwork.cache_url(url)
|
||||||
|
|
|
@ -13,7 +13,7 @@ from .plex_db import PlexDB
|
||||||
from . import plex_functions as PF
|
from . import plex_functions as PF
|
||||||
from . import utils
|
from . import utils
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
from . import kodidb_functions as kodidb
|
from .kodi_db import KodiVideoDB
|
||||||
from . import playlist_func as PL
|
from . import playlist_func as PL
|
||||||
from . import playqueue as PQ
|
from . import playqueue as PQ
|
||||||
from . import json_rpc as js
|
from . import json_rpc as js
|
||||||
|
@ -429,8 +429,8 @@ def _conclude_playback(playqueue, pos):
|
||||||
with PlexDB() as plexdb:
|
with PlexDB() as plexdb:
|
||||||
db_item = plexdb.item_by_id(item.plex_id, item.plex_type)
|
db_item = plexdb.item_by_id(item.plex_id, item.plex_type)
|
||||||
file_id = db_item['kodi_fileid'] if db_item else None
|
file_id = db_item['kodi_fileid'] if db_item else None
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
item.offset = kodi_db.get_resume(file_id)
|
item.offset = kodidb.get_resume(file_id)
|
||||||
LOG.info('Resuming playback at %s', item.offset)
|
LOG.info('Resuming playback at %s', item.offset)
|
||||||
if v.KODIVERSION >= 18 and api:
|
if v.KODIVERSION >= 18 and api:
|
||||||
# Kodi 18 Alpha 3 broke StartOffset
|
# Kodi 18 Alpha 3 broke StartOffset
|
||||||
|
|
|
@ -5,11 +5,11 @@ from logging import getLogger
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from urlparse import parse_qsl
|
from urlparse import parse_qsl
|
||||||
|
|
||||||
|
from .kodi_db import KodiVideoDB
|
||||||
from . import playback
|
from . import playback
|
||||||
from . import context_entry
|
from . import context_entry
|
||||||
from . import json_rpc as js
|
from . import json_rpc as js
|
||||||
from . import pickler
|
from . import pickler
|
||||||
from . import kodidb_functions as kodidb
|
|
||||||
from . import state
|
from . import state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -47,8 +47,8 @@ class PlaybackStarter(Thread):
|
||||||
resolve=resolve)
|
resolve=resolve)
|
||||||
elif mode == 'navigation':
|
elif mode == 'navigation':
|
||||||
# e.g. when plugin://...tvshows is called for entire season
|
# e.g. when plugin://...tvshows is called for entire season
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
show_id = kodi_db.show_id_from_path(params.get('path'))
|
show_id = kodidb.show_id_from_path(params.get('path'))
|
||||||
if show_id:
|
if show_id:
|
||||||
js.activate_window('videos',
|
js.activate_window('videos',
|
||||||
'videodb://tvshows/titles/%s' % show_id)
|
'videodb://tvshows/titles/%s' % show_id)
|
||||||
|
|
|
@ -11,7 +11,7 @@ from urlparse import parse_qsl, urlsplit
|
||||||
from .plex_api import API
|
from .plex_api import API
|
||||||
from .plex_db import PlexDB
|
from .plex_db import PlexDB
|
||||||
from . import plex_functions as PF
|
from . import plex_functions as PF
|
||||||
from . import kodidb_functions as kodidb
|
from .kodi_db import kodiid_from_filename
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
from . import utils
|
from . import utils
|
||||||
from . import json_rpc as js
|
from . import json_rpc as js
|
||||||
|
@ -360,7 +360,7 @@ def verify_kodi_item(plex_id, kodi_item):
|
||||||
if (not state.DIRECT_PATHS and state.ENABLE_MUSIC and
|
if (not state.DIRECT_PATHS and state.ENABLE_MUSIC and
|
||||||
kodi_item.get('type') == v.KODI_TYPE_SONG and
|
kodi_item.get('type') == v.KODI_TYPE_SONG and
|
||||||
kodi_item['file'].startswith('http')):
|
kodi_item['file'].startswith('http')):
|
||||||
kodi_item['id'], _ = kodidb.kodiid_from_filename(kodi_item['file'],
|
kodi_item['id'], _ = kodiid_from_filename(kodi_item['file'],
|
||||||
v.KODI_TYPE_SONG)
|
v.KODI_TYPE_SONG)
|
||||||
LOG.debug('Detected song. Research results: %s', kodi_item)
|
LOG.debug('Detected song. Research results: %s', kodi_item)
|
||||||
return kodi_item
|
return kodi_item
|
||||||
|
@ -372,15 +372,15 @@ def verify_kodi_item(plex_id, kodi_item):
|
||||||
raise PlaylistError
|
raise PlaylistError
|
||||||
LOG.debug('Starting research for Kodi id since we didnt get one: %s',
|
LOG.debug('Starting research for Kodi id since we didnt get one: %s',
|
||||||
kodi_item)
|
kodi_item)
|
||||||
kodi_id, _ = kodidb.kodiid_from_filename(kodi_item['file'],
|
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
|
||||||
v.KODI_TYPE_MOVIE)
|
v.KODI_TYPE_MOVIE)
|
||||||
kodi_item['type'] = v.KODI_TYPE_MOVIE
|
kodi_item['type'] = v.KODI_TYPE_MOVIE
|
||||||
if kodi_id is None:
|
if kodi_id is None:
|
||||||
kodi_id, _ = kodidb.kodiid_from_filename(kodi_item['file'],
|
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
|
||||||
v.KODI_TYPE_EPISODE)
|
v.KODI_TYPE_EPISODE)
|
||||||
kodi_item['type'] = v.KODI_TYPE_EPISODE
|
kodi_item['type'] = v.KODI_TYPE_EPISODE
|
||||||
if kodi_id is None:
|
if kodi_id is None:
|
||||||
kodi_id, _ = kodidb.kodiid_from_filename(kodi_item['file'],
|
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
|
||||||
v.KODI_TYPE_SONG)
|
v.KODI_TYPE_SONG)
|
||||||
kodi_item['type'] = v.KODI_TYPE_SONG
|
kodi_item['type'] = v.KODI_TYPE_SONG
|
||||||
kodi_item['id'] = kodi_id
|
kodi_item['id'] = kodi_id
|
||||||
|
|
|
@ -9,7 +9,7 @@ from logging import getLogger
|
||||||
|
|
||||||
from .common import Playlist, PlaylistError
|
from .common import Playlist, PlaylistError
|
||||||
from ..plex_db import PlexDB
|
from ..plex_db import PlexDB
|
||||||
from .. import kodidb_functions as kodidb
|
from ..kodi_db import kodiid_from_filename
|
||||||
from .. import path_ops, utils, variables as v
|
from .. import path_ops, utils, variables as v
|
||||||
###############################################################################
|
###############################################################################
|
||||||
LOG = getLogger('PLEX.playlists.db')
|
LOG = getLogger('PLEX.playlists.db')
|
||||||
|
@ -88,8 +88,8 @@ def m3u_to_plex_ids(playlist):
|
||||||
plex_ids.append(plex_id)
|
plex_ids.append(plex_id)
|
||||||
else:
|
else:
|
||||||
# Add-on paths not working, try direct
|
# Add-on paths not working, try direct
|
||||||
kodi_id, kodi_type = kodidb.kodiid_from_filename(
|
kodi_id, kodi_type = kodiid_from_filename(entry,
|
||||||
entry, db_type=playlist.kodi_type)
|
db_type=playlist.kodi_type)
|
||||||
if not kodi_id:
|
if not kodi_id:
|
||||||
continue
|
continue
|
||||||
with PlexDB() as plexdb:
|
with PlexDB() as plexdb:
|
||||||
|
|
|
@ -38,13 +38,13 @@ from urlparse import parse_qsl
|
||||||
from xbmcgui import ListItem
|
from xbmcgui import ListItem
|
||||||
|
|
||||||
from .plex_db import PlexDB
|
from .plex_db import PlexDB
|
||||||
|
from .kodi_db import KodiVideoDB, KodiMusicDB
|
||||||
from .utils import cast
|
from .utils import cast
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
from . import clientinfo
|
from . import clientinfo
|
||||||
from . import utils
|
from . import utils
|
||||||
from . import path_ops
|
from . import path_ops
|
||||||
from . import plex_functions as PF
|
from . import plex_functions as PF
|
||||||
from . import kodidb_functions as kodidb
|
|
||||||
from . import variables as v
|
from . import variables as v
|
||||||
from . import state
|
from . import state
|
||||||
|
|
||||||
|
@ -939,13 +939,13 @@ class API(object):
|
||||||
else:
|
else:
|
||||||
return artworks
|
return artworks
|
||||||
# Grab artwork from the season
|
# Grab artwork from the season
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
season_art = kodi_db.get_art(season_id, v.KODI_TYPE_SEASON)
|
season_art = kodidb.get_art(season_id, v.KODI_TYPE_SEASON)
|
||||||
for kodi_art in season_art:
|
for kodi_art in season_art:
|
||||||
artworks['season.%s' % kodi_art] = season_art[kodi_art]
|
artworks['season.%s' % kodi_art] = season_art[kodi_art]
|
||||||
# Grab more artwork from the show
|
# Grab more artwork from the show
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
show_art = kodi_db.get_art(show_id, v.KODI_TYPE_SHOW)
|
show_art = kodidb.get_art(show_id, v.KODI_TYPE_SHOW)
|
||||||
for kodi_art in show_art:
|
for kodi_art in show_art:
|
||||||
artworks['tvshow.%s' % kodi_art] = show_art[kodi_art]
|
artworks['tvshow.%s' % kodi_art] = show_art[kodi_art]
|
||||||
return artworks
|
return artworks
|
||||||
|
@ -953,11 +953,11 @@ class API(object):
|
||||||
if kodi_id:
|
if kodi_id:
|
||||||
# in Kodi database, potentially with additional e.g. clearart
|
# in Kodi database, potentially with additional e.g. clearart
|
||||||
if self.plex_type() in v.PLEX_VIDEOTYPES:
|
if self.plex_type() in v.PLEX_VIDEOTYPES:
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with KodiVideoDB() as kodidb:
|
||||||
return kodi_db.get_art(kodi_id, kodi_type)
|
return kodidb.get_art(kodi_id, kodi_type)
|
||||||
else:
|
else:
|
||||||
with kodidb.GetKodiDB('music') as kodi_db:
|
with KodiMusicDB() as kodidb:
|
||||||
return kodi_db.get_art(kodi_id, kodi_type)
|
return kodidb.get_art(kodi_id, kodi_type)
|
||||||
|
|
||||||
# Grab artwork from Plex
|
# Grab artwork from Plex
|
||||||
# if self.plex_type() == v.PLEX_TYPE_EPISODE:
|
# if self.plex_type() == v.PLEX_TYPE_EPISODE:
|
||||||
|
|
|
@ -7,7 +7,7 @@ import xbmc
|
||||||
from .downloadutils import DownloadUtils as DU
|
from .downloadutils import DownloadUtils as DU
|
||||||
from . import library_sync
|
from . import library_sync
|
||||||
from . import backgroundthread, utils, path_ops, artwork, variables as v, state
|
from . import backgroundthread, utils, path_ops, artwork, variables as v, state
|
||||||
from . import plex_db, kodidb_functions as kodidb
|
from . import plex_db, kodi_db
|
||||||
|
|
||||||
LOG = getLogger('PLEX.sync')
|
LOG = getLogger('PLEX.sync')
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ class Sync(backgroundthread.KillableThread):
|
||||||
return
|
return
|
||||||
# ask to reset all existing or not
|
# ask to reset all existing or not
|
||||||
if utils.yesno_dialog('Image Texture Cache', utils.lang(39251)):
|
if utils.yesno_dialog('Image Texture Cache', utils.lang(39251)):
|
||||||
kodidb.reset_cached_images()
|
kodi_db.reset_cached_images()
|
||||||
self.start_image_cache_thread()
|
self.start_image_cache_thread()
|
||||||
|
|
||||||
def on_library_scan_finished(self, successful):
|
def on_library_scan_finished(self, successful):
|
||||||
|
@ -236,9 +236,9 @@ class Sync(backgroundthread.KillableThread):
|
||||||
plex_db.initialize()
|
plex_db.initialize()
|
||||||
# Hack to speed up look-ups for actors (giant table!)
|
# Hack to speed up look-ups for actors (giant table!)
|
||||||
utils.create_kodi_db_indicees()
|
utils.create_kodi_db_indicees()
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodi_db.KodiVideoDB() as kodidb:
|
||||||
# Setup the paths for addon-paths (even when using direct paths)
|
# Setup the paths for addon-paths (even when using direct paths)
|
||||||
kodi_db.setup_path_table()
|
kodidb.setup_path_table()
|
||||||
|
|
||||||
while not self.isCanceled():
|
while not self.isCanceled():
|
||||||
# In the event the server goes offline
|
# In the event the server goes offline
|
||||||
|
|
|
@ -526,8 +526,7 @@ def wipe_database():
|
||||||
delete_playlists()
|
delete_playlists()
|
||||||
# Clean up the video nodes
|
# Clean up the video nodes
|
||||||
delete_nodes()
|
delete_nodes()
|
||||||
from . import kodidb_functions
|
from . import kodi_db, plex_db
|
||||||
from . import plex_db
|
|
||||||
# First get the paths to all synced playlists
|
# First get the paths to all synced playlists
|
||||||
playlist_paths = []
|
playlist_paths = []
|
||||||
try:
|
try:
|
||||||
|
@ -545,7 +544,7 @@ def wipe_database():
|
||||||
# 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'
|
||||||
kodidb_functions.wipe_dbs(music)
|
kodi_db.wipe_dbs(music)
|
||||||
plex_db.wipe()
|
plex_db.wipe()
|
||||||
plex_db.initialize()
|
plex_db.initialize()
|
||||||
# Delete all synced playlists
|
# Delete all synced playlists
|
||||||
|
@ -558,7 +557,7 @@ def wipe_database():
|
||||||
|
|
||||||
LOG.info("Resetting all cached artwork.")
|
LOG.info("Resetting all cached artwork.")
|
||||||
# Remove all cached artwork
|
# Remove all cached artwork
|
||||||
kodidb_functions.reset_cached_images()
|
kodi_db.reset_cached_images()
|
||||||
# reset the install run flag
|
# reset the install run flag
|
||||||
settings('SyncInstallRunDone', value="false")
|
settings('SyncInstallRunDone', value="false")
|
||||||
LOG.info('Wiping done')
|
LOG.info('Wiping done')
|
||||||
|
|
Loading…
Reference in a new issue