Big Kodi DB overhaul - ensure video metadata updates/deletes correctly

This commit is contained in:
croneter 2018-02-28 17:24:32 +01:00
parent f6336feb72
commit f4681011b9
3 changed files with 199 additions and 473 deletions

View file

@ -53,13 +53,6 @@ LOG = getLogger("PLEX." + __name__)
REGEX_IMDB = re_compile(r'''/(tt\d+)''') REGEX_IMDB = re_compile(r'''/(tt\d+)''')
REGEX_TVDB = re_compile(r'''thetvdb:\/\/(.+?)\?''') REGEX_TVDB = re_compile(r'''thetvdb:\/\/(.+?)\?''')
# Key of library: Plex-identifier. Value represents the Kodi/emby side
PEOPLE_OF_INTEREST = {
'Director': 'Director',
'Writer': 'Writer',
'Role': 'Actor',
'Producer': 'Producer'
}
# we need to use a little mapping between fanart.tv arttypes and kodi # we need to use a little mapping between fanart.tv arttypes and kodi
# artttypes # artttypes
FANART_TV_TYPES = [ FANART_TV_TYPES = [
@ -326,25 +319,35 @@ class API(object):
def people_list(self): def people_list(self):
""" """
Returns a list of people from item, with a list item of the form Returns a dict with lists of tuples:
{ {
'Name': xxx, 'actor': [..., (<name>, <artwork url>, <role>, <cast order>), ...],
'Type': xxx, 'director': [..., (<name>, ), ...],
'Id': xxx 'writer': [..., (<name>, ), ...]
'imageurl': url to picture, None otherwise
('Role': xxx for cast/actors only, None if not found)
} }
Everything in unicode, except <cast order> which is an int.
Only <art-url> and <role> may be None if not found.
Kodi does not yet support a Producer. People may appear several times
per category and overall!
""" """
people = [] people = {
'actor': [],
'director': [],
'writer': []
}
cast_order = 0
for child in self.item: for child in self.item:
if child.tag in PEOPLE_OF_INTEREST: if child.tag == 'Role':
people.append({ people['actor'].append((child.attrib['tag'],
'Name': child.attrib['tag'], child.get('thumb'),
'Type': PEOPLE_OF_INTEREST[child.tag], child.get('role'),
'Id': child.attrib['id'], cast_order))
'imageurl': child.get('thumb'), cast_order += 1
'Role': child.get('role') elif child.tag == 'Writer':
}) people['writer'].append((child.attrib['tag'], ))
elif child.tag == 'Director':
people['director'].append((child.attrib['tag'], ))
return people return people
def genre_list(self): def genre_list(self):

View file

@ -439,7 +439,9 @@ class Movies(Items):
# Process countries # Process countries
self.kodi_db.modify_countries(movieid, v.KODI_TYPE_MOVIE, countries) self.kodi_db.modify_countries(movieid, v.KODI_TYPE_MOVIE, countries)
# Process cast # Process cast
self.kodi_db.addPeople(movieid, api.people_list(), "movie") self.kodi_db.modify_people(movieid,
v.KODI_TYPE_MOVIE,
api.people_list())
# Process genres # Process genres
self.kodi_db.modify_genres(movieid, v.KODI_TYPE_MOVIE, genres) self.kodi_db.modify_genres(movieid, v.KODI_TYPE_MOVIE, genres)
# Process artwork # Process artwork
@ -472,8 +474,8 @@ class Movies(Items):
kodi_id = plex_dbitem[0] kodi_id = plex_dbitem[0]
file_id = plex_dbitem[1] file_id = plex_dbitem[1]
kodi_type = plex_dbitem[4] kodi_type = plex_dbitem[4]
LOG.debug("Removing %sid: %s file_id: %s", LOG.debug('Removing %sid: %s file_id: %s',
kodi_type, kodi_id, file_id) kodi_type, kodi_id, file_id)
except TypeError: except TypeError:
return return
@ -484,11 +486,11 @@ class Movies(Items):
if kodi_type == v.KODI_TYPE_MOVIE: if kodi_type == v.KODI_TYPE_MOVIE:
set_id = self.kodi_db.get_set_id(kodi_id) set_id = self.kodi_db.get_set_id(kodi_id)
self.kodi_db.delete_countries(kodi_id, kodi_type) self.kodi_db.modify_countries(kodi_id, kodi_type)
self.kodi_db.delete_people(kodi_id, kodi_type) self.kodi_db.modify_people(kodi_id, kodi_type)
self.kodi_db.delete_genre(kodi_id, kodi_type) self.kodi_db.modify_genres(kodi_id, kodi_type)
self.kodi_db.delete_studios(kodi_id, kodi_type) self.kodi_db.modify_studios(kodi_id, kodi_type)
self.kodi_db.delete_tags(kodi_id, kodi_type) self.kodi_db.modify_tags(kodi_id, kodi_type)
self.kodi_db.modify_streams(file_id) self.kodi_db.modify_streams(file_id)
self.kodi_db.delete_playstate(file_id) self.kodi_db.delete_playstate(file_id)
# Delete kodi movie and file # Delete kodi movie and file
@ -739,20 +741,15 @@ class TVShows(Items):
''' '''
kodicursor.execute(query, (path, None, None, 1, toppathid, pathid)) kodicursor.execute(query, (path, None, None, 1, toppathid, pathid))
# Process cast self.kodi_db.modify_people(showid, v.KODI_TYPE_SHOW, api.people_list())
people = api.people_list() self.kodi_db.modify_genres(showid, v.KODI_TYPE_SHOW, genres)
self.kodi_db.addPeople(showid, people, "tvshow") artwork.addArtwork(api.artwork(), showid, v.KODI_TYPE_SHOW, kodicursor)
# Process genres
self.kodi_db.addGenres(showid, genres, "tvshow")
# Process artwork
allartworks = api.artwork()
artwork.addArtwork(allartworks, showid, "tvshow", kodicursor)
# Process studios # Process studios
self.kodi_db.addStudios(showid, studios, "tvshow") self.kodi_db.modify_studios(showid, v.KODI_TYPE_SHOW, studios)
# Process tags: view, PMS collection tags # Process tags: view, PMS collection tags
tags = [viewtag] tags = [viewtag]
tags.extend(collections) tags.extend(collections)
self.kodi_db.addTags(showid, tags, "tvshow") self.kodi_db.modify_tags(showid, v.KODI_TYPE_SHOW, tags)
@catch_exceptions(warnuser=True) @catch_exceptions(warnuser=True)
def add_updateSeason(self, item, viewtag=None, viewid=None): def add_updateSeason(self, item, viewtag=None, viewid=None):
@ -1088,8 +1085,9 @@ class TVShows(Items):
)) ))
kodicursor.execute(query, (pathid, filename, dateadded, fileid)) kodicursor.execute(query, (pathid, filename, dateadded, fileid))
# Process cast # Process cast
people = api.people_list() self.kodi_db.modify_people(episodeid,
self.kodi_db.addPeople(episodeid, people, "episode") v.KODI_TYPE_EPISODE,
api.people_list())
# Process artwork # Process artwork
# Wide "screenshot" of particular episode # Wide "screenshot" of particular episode
poster = item.attrib.get('thumb') poster = item.attrib.get('thumb')
@ -1221,9 +1219,9 @@ class TVShows(Items):
Remove a TV show, and only the show, no seasons or episodes Remove a TV show, and only the show, no seasons or episodes
""" """
kodicursor = self.kodicursor kodicursor = self.kodicursor
self.kodi_db.delete_genre(kodi_id, v.KODI_TYPE_SHOW) self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW)
self.kodi_db.delete_studios(kodi_id, v.KODI_TYPE_SHOW) self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW)
self.kodi_db.delete_tags(kodi_id, v.KODI_TYPE_SHOW) self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW)
self.artwork.deleteArtwork(kodi_id, v.KODI_TYPE_SHOW, kodicursor) self.artwork.deleteArtwork(kodi_id, v.KODI_TYPE_SHOW, kodicursor)
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,)) kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,))
if v.KODIVERSION >= 17: if v.KODIVERSION >= 17:
@ -1246,7 +1244,7 @@ class TVShows(Items):
Remove an episode, and episode only Remove an episode, and episode only
""" """
kodicursor = self.kodicursor kodicursor = self.kodicursor
self.kodi_db.delete_people(kodi_id, v.KODI_TYPE_EPISODE) self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
self.kodi_db.modify_streams(file_id) self.kodi_db.modify_streams(file_id)
self.kodi_db.delete_playstate(file_id) self.kodi_db.delete_playstate(file_id)
self.artwork.deleteArtwork(kodi_id, "episode", kodicursor) self.artwork.deleteArtwork(kodi_id, "episode", kodicursor)

View file

@ -3,6 +3,7 @@
############################################################################### ###############################################################################
from logging import getLogger from logging import getLogger
from ntpath import dirname from ntpath import dirname
from sqlite3 import IntegrityError
import artwork import artwork
from utils import kodi_sql, try_decode from utils import kodi_sql, try_decode
@ -287,14 +288,15 @@ class KodiDBMethods(object):
self.cursor.execute(query_rem, (entry_id,)) self.cursor.execute(query_rem, (entry_id,))
if self.cursor.fetchone() is None: if self.cursor.fetchone() is None:
# Delete in the original table because entry is now orphaned # Delete in the original table because entry is now orphaned
LOG.debug('Deleting %s from Kodi DB: %s', table, entry_id) LOG.debug('Removing %s from Kodi DB: %s', table, entry_id)
self.cursor.execute(query_delete, (entry_id,)) self.cursor.execute(query_delete, (entry_id,))
def modify_countries(self, kodi_id, kodi_type, countries): def modify_countries(self, kodi_id, kodi_type, countries=None):
""" """
Writes a country (string) in the list countries into the Kodi DB. Will Writes a country (string) in the list countries into the Kodi DB. Will
also delete any orphaned country entries. also delete any orphaned country entries.
""" """
countries = countries if countries else []
self._modify_link_and_table(kodi_id, self._modify_link_and_table(kodi_id,
kodi_type, kodi_type,
countries, countries,
@ -302,11 +304,12 @@ class KodiDBMethods(object):
'country', 'country',
'country_id') 'country_id')
def modify_genres(self, kodi_id, kodi_type, genres): def modify_genres(self, kodi_id, kodi_type, genres=None):
""" """
Writes a country (string) in the list countries into the Kodi DB. Will Writes a country (string) in the list countries into the Kodi DB. Will
also delete any orphaned country entries. also delete any orphaned country entries.
""" """
genres = genres if genres else []
self._modify_link_and_table(kodi_id, self._modify_link_and_table(kodi_id,
kodi_type, kodi_type,
genres, genres,
@ -314,11 +317,12 @@ class KodiDBMethods(object):
'genre', 'genre',
'genre_id') 'genre_id')
def modify_studios(self, kodi_id, kodi_type, studios): def modify_studios(self, kodi_id, kodi_type, studios=None):
""" """
Writes a country (string) in the list countries into the Kodi DB. Will Writes a country (string) in the list countries into the Kodi DB. Will
also delete any orphaned country entries. also delete any orphaned country entries.
""" """
studios = studios if studios else []
self._modify_link_and_table(kodi_id, self._modify_link_and_table(kodi_id,
kodi_type, kodi_type,
studios, studios,
@ -326,11 +330,12 @@ class KodiDBMethods(object):
'studio', 'studio',
'studio_id') 'studio_id')
def modify_tags(self, kodi_id, kodi_type, tags): def modify_tags(self, kodi_id, kodi_type, tags=None):
""" """
Writes a country (string) in the list countries into the Kodi DB. Will Writes a country (string) in the list countries into the Kodi DB. Will
also delete any orphaned country entries. also delete any orphaned country entries.
""" """
tags = tags if tags else []
self._modify_link_and_table(kodi_id, self._modify_link_and_table(kodi_id,
kodi_type, kodi_type,
tags, tags,
@ -338,201 +343,123 @@ class KodiDBMethods(object):
'tag', 'tag',
'tag_id') 'tag_id')
def addCountries(self, kodiid, countries, mediatype): def modify_people(self, kodi_id, kodi_type, people=None):
for country in countries: """
query = ' '.join(( Makes sure that actors, directors and writers are recorded correctly
for the elmement kodi_id, kodi_type.
"SELECT country_id", Will also delete a freshly orphaned actor entry.
"FROM country", """
"WHERE name = ?", people = people if people else {'actor': [],
"COLLATE NOCASE" 'director': [],
)) 'writer': []}
self.cursor.execute(query, (country,)) for kind, people_list in people.iteritems():
self._modify_people_kind(kodi_id, kodi_type, kind, people_list)
def _modify_people_kind(self, kodi_id, kodi_type, kind, people_list):
# Get the people already saved in the DB for this specific item
if kind == 'actor':
query = '''
SELECT actor.actor_id, actor.name, art.url, actor_link.role,
actor_link.cast_order
FROM actor_link
LEFT JOIN actor ON actor.actor_id = actor_link.actor_id
LEFT JOIN art ON (art.media_id = actor_link.actor_id AND
art.media_type = 'actor')
WHERE actor_link.media_id = ? AND actor_link.media_type = ?
'''
else:
query = '''
SELECT actor.actor_id, actor.name
FROM {0}_link
LEFT JOIN actor ON actor.actor_id = {0}_link.actor_id
WHERE {0}_link.media_id = ? AND {0}_link.media_type = ?
'''.format(kind)
self.cursor.execute(query, (kodi_id, kodi_type))
old_people = self.cursor.fetchall()
# Determine which people we need to save or delete
outdated_people = []
for person in old_people:
try: try:
country_id = self.cursor.fetchone()[0] people_list.remove(person[1:])
except ValueError:
outdated_people.append(person)
# Get rid of old entries
query = '''
DELETE FROM %s_link
WHERE actor_id = ? AND media_id = ? AND media_type = ?
''' % kind
query_actor_check = 'SELECT actor_id FROM %s_link WHERE actor_id = ?'
query_actor_delete = 'DELETE FROM actor WHERE actor_id = ?'
for person in outdated_people:
# Delete the outdated entry
self.cursor.execute(query, (person[0], kodi_id, kodi_type))
# Do we now have orphaned entries?
for person_kind in ('actor', 'writer', 'director'):
self.cursor.execute(query_actor_check % person_kind,
(person[0],))
if self.cursor.fetchone() is not None:
break
else:
# person entry in actor table is now orphaned
# Delete the person from actor table
LOG.debug('Removing person from Kodi DB: %s', person)
self.cursor.execute(query_actor_delete, (person[0],))
if kind == 'actor':
# Delete any associated artwork
self.artwork.deleteArtwork(person[0], 'actor', self.cursor)
# Save new people to Kodi DB by iterating over the remaining entries
if kind == 'actor':
query = 'INSERT INTO actor_link VALUES (?, ?, ?, ?, ?)'
for person in people_list:
LOG.debug('Adding actor to Kodi DB: %s', person)
# Make sure the person entry in table actor exists
actor_id = self._get_actor_id(person[0], art_url=person[1])
# Link the person with the media element
try:
self.cursor.execute(query, (actor_id, kodi_id, kodi_type,
person[2], person[3]))
except IntegrityError:
# With Kodi, an actor may have only one role, unlike Plex
pass
else:
query = 'INSERT INTO %s_link VALUES (?, ?, ?)' % kind
for person in people_list:
LOG.debug('Adding %s to Kodi DB: %s', kind, person[0])
# Make sure the person entry in table actor exists:
actor_id = self._get_actor_id(person[0])
# Link the person with the media element
try:
self.cursor.execute(query, (actor_id, kodi_id, kodi_type))
except IntegrityError:
# Again, Kodi may have only one person assigned to a role
pass
except TypeError: def _get_actor_id(self, name, art_url=None):
# Country entry does not exists
self.cursor.execute("select coalesce(max(country_id),0) from country")
country_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO country(country_id, name) values(?, ?)"
self.cursor.execute(query, (country_id, country))
LOG.debug("Add country to media, processing: %s", country)
finally: # Assign country to content
query = (
'''
INSERT OR REPLACE INTO country_link(
country_id, media_id, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (country_id, kodiid, mediatype))
def _delete_from_link_and_table(self, kodi_id, kodi_type, link_table,
table, key):
# Get all existing links
query = ('SELECT %s FROM %s WHERE media_id = ? AND media_type = ? '
% (key, link_table))
self.cursor.execute(query, (kodi_id, kodi_type))
key_list = self.cursor.fetchall()
# Delete all links
query = ('DELETE FROM %s WHERE media_id = ? AND media_type = ?'
% link_table)
self.cursor.execute(query, (kodi_id, kodi_type))
# Which countries are now orphaned?
query = 'SELECT %s FROM %s WHERE %s = ?' % (key, link_table, key)
query_delete = 'DELETE FROM %s WHERE %s = ?' % (table, key)
for entry in key_list:
# country_id still in table?
self.cursor.execute(query, (entry[0],))
if self.cursor.fetchone() is None:
self.cursor.execute(query_delete, (entry[0],))
def delete_countries(self, kodi_id, kodi_type):
""" """
Assuming that video kodi_id, kodi_type gets deleted, will delete any Returns the actor_id [int] for name [unicode] in table actor (without
associated country links in the table country_link and also deletes ensuring that the name matches).
orphaned countries in the table country If not, will create a new record with actor_id, name, art_url
"""
self._delete_from_link_and_table(kodi_id,
kodi_type,
'country_link',
'country',
'country_id')
def _getactorid(self, name): Uses Plex ids and thus assumes that Plex person id is unique!
""" """
Crucial für sync speed! self.cursor.execute('SELECT actor_id FROM actor WHERE name=? LIMIT 1',
""" (name,))
query = ' '.join((
"SELECT actor_id",
"FROM actor",
"WHERE name = ?",
"LIMIT 1"
))
self.cursor.execute(query, (name,))
try: try:
actorid = self.cursor.fetchone()[0] actor_id = self.cursor.fetchone()[0]
except TypeError: except TypeError:
# Cast entry does not exists # Not yet in actor DB, add person
self.cursor.execute("select coalesce(max(actor_id),0) from actor") self.cursor.execute('SELECT COALESCE(MAX(actor_id),-1) FROM actor')
actorid = self.cursor.fetchone()[0] + 1 actor_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO actor(actor_id, name) VALUES (?, ?)" self.cursor.execute('INSERT INTO actor(actor_id, name) '
self.cursor.execute(query, (actorid, name)) 'VALUES (?, ?)',
return actorid (actor_id, name))
if art_url:
def _addPerson(self, role, person_type, actorid, kodiid, mediatype, self.artwork.addOrUpdateArt(art_url,
castorder): actor_id,
if "Actor" == person_type: 'actor',
query = ''' "thumb",
INSERT OR REPLACE INTO actor_link(
actor_id, media_id, media_type, role, cast_order)
VALUES (?, ?, ?, ?, ?)
'''
self.cursor.execute(query, (actorid, kodiid, mediatype, role,
castorder))
castorder += 1
elif "Director" == person_type:
query = '''
INSERT OR REPLACE INTO director_link(
actor_id, media_id, media_type)
VALUES (?, ?, ?)
'''
self.cursor.execute(query, (actorid, kodiid, mediatype))
elif person_type == "Writer":
query = '''
INSERT OR REPLACE INTO writer_link(
actor_id, media_id, media_type)
VALUES (?, ?, ?)
'''
self.cursor.execute(query, (actorid, kodiid, mediatype))
elif "Artist" == person_type:
query = '''
INSERT OR REPLACE INTO actor_link(
actor_id, media_id, media_type)
VALUES (?, ?, ?)
'''
self.cursor.execute(query, (actorid, kodiid, mediatype))
return castorder
def addPeople(self, kodiid, people, mediatype):
castorder = 0
for person in people:
actorid = self._getactorid(person['Name'])
# Link person to content
castorder = self._addPerson(person.get('Role'),
person['Type'],
actorid,
kodiid,
mediatype,
castorder)
# Add person image to art table
if person['imageurl']:
self.artwork.addOrUpdateArt(person['imageurl'], actorid,
person['Type'].lower(), "thumb",
self.cursor) self.cursor)
return actor_id
def delete_people(self, kodi_id, kodi_type):
"""
Assuming that the video kodi_id, kodi_type gets deleted, will delete any
associated actor_, director_, writer_links and also deletes
orphaned actors
"""
# Actors
query = '''
SELECT actor_id FROM actor_link
WHERE media_id = ? AND media_type = ?
'''
self.cursor.execute(query, (kodi_id, kodi_type))
actor_ids = self.cursor.fetchall()
query = 'DELETE FROM actor_link WHERE media_id = ? AND media_type = ?'
self.cursor.execute(query, (kodi_id, kodi_type))
# Directors
query = '''
SELECT actor_id FROM director_link
WHERE media_id = ? AND media_type = ?
'''
self.cursor.execute(query, (kodi_id, kodi_type))
actor_ids.extend(self.cursor.fetchall())
query = '''
DELETE FROM director_link WHERE media_id = ? AND media_type = ?
'''
self.cursor.execute(query, (kodi_id, kodi_type))
# Writers
query = '''
SELECT actor_id FROM writer_link
WHERE media_id = ? AND media_type = ?
'''
self.cursor.execute(query, (kodi_id, kodi_type))
actor_ids.extend(self.cursor.fetchall())
query = '''
DELETE FROM writer_link WHERE media_id = ? AND media_type = ?
'''
self.cursor.execute(query, (kodi_id, kodi_type))
# Which people are now orphaned?
query_actor = 'SELECT actor_id FROM actor_link WHERE actor_id = ?'
query_director = 'SELECT actor_id FROM director_link WHERE actor_id = ?'
query_writer = 'SELECT actor_id FROM writer_link WHERE actor_id = ?'
query_delete = 'DELETE FROM actor WHERE actor_id = ?'
# Delete orphaned people
for actor_id in actor_ids:
self.cursor.execute(query_actor, (actor_id[0],))
if self.cursor.fetchone() is None:
self.cursor.execute(query_director, (actor_id[0],))
if self.cursor.fetchone() is None:
self.cursor.execute(query_writer, (actor_id[0],))
if self.cursor.fetchone() is None:
# Delete the person itself from actor table
self.cursor.execute(query_delete, (actor_id[0],))
# Delete any associated artwork
self.artwork.deleteArtwork(actor_id[0],
'actor',
self.cursor)
def existingArt(self, kodiId, mediaType, refresh=False): def existingArt(self, kodiId, mediaType, refresh=False):
""" """
@ -576,134 +503,46 @@ class KodiDBMethods(object):
result['Backdrop'] = [d[0] for d in data] result['Backdrop'] = [d[0] for d in data]
return result return result
def addGenres(self, kodi_id, genres, kodi_type):
"""
Adds the genres (list of strings) to the Kodi DB and associates them
with the element kodi_id, kodi_type
"""
# Delete current genres for clean slate
query = 'DELETE FROM genre_link WHERE media_id = ? AND media_type = ?'
self.cursor.execute(query, (kodi_id, kodi_type,))
# Add genres
for genre in genres:
query = ' SELECT genre_id FROM genre WHERE name = ? COLLATE NOCASE'
self.cursor.execute(query, (genre,))
try:
genre_id = self.cursor.fetchone()[0]
except TypeError:
# Create genre in database
self.cursor.execute("select coalesce(max(genre_id),0) from genre")
genre_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
self.cursor.execute(query, (genre_id, genre))
finally:
# Assign genre to item
query = '''
INSERT OR REPLACE INTO genre_link(
genre_id, media_id, media_type)
VALUES (?, ?, ?)
'''
self.cursor.execute(query, (genre_id, kodi_id, kodi_type))
def delete_genre(self, kodi_id, kodi_type):
"""
Removes the genre links as well as orphaned genres from the Kodi DB
"""
self._delete_from_link_and_table(kodi_id,
kodi_type,
'genre_link',
'genre',
'genre_id')
def addStudios(self, kodiid, studios, mediatype):
for studio in studios:
query = ' '.join((
"SELECT studio_id",
"FROM studio",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (studio,))
try:
studioid = self.cursor.fetchone()[0]
except TypeError:
# Studio does not exists.
self.cursor.execute("select coalesce(max(studio_id),0) from studio")
studioid = self.cursor.fetchone()[0] + 1
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
self.cursor.execute(query, (studioid, studio))
LOG.debug("Add Studios to media, processing: %s", studio)
finally: # Assign studio to item
query = (
'''
INSERT OR REPLACE INTO studio_link(
studio_id, media_id, media_type)
VALUES (?, ?, ?)
''')
self.cursor.execute(query, (studioid, kodiid, mediatype))
def delete_studios(self, kodi_id, kodi_type):
"""
Removes the studio links as well as orphaned studios from the Kodi DB
"""
self._delete_from_link_and_table(kodi_id,
kodi_type,
'studio_link',
'studio',
'studio_id')
def modify_streams(self, fileid, streamdetails=None, runtime=None): def modify_streams(self, fileid, streamdetails=None, runtime=None):
""" """
Leave streamdetails and runtime empty to delete all stream entries for Leave streamdetails and runtime empty to delete all stream entries for
fileid fileid
""" """
# First remove any existing entries # First remove any existing entries
self.cursor.execute("DELETE FROM streamdetails WHERE idFile = ?", (fileid,)) self.cursor.execute('DELETE FROM streamdetails WHERE idFile = ?',
if streamdetails: (fileid,))
# Video details if not streamdetails:
for videotrack in streamdetails['video']: return
query = ( for videotrack in streamdetails['video']:
''' query = '''
INSERT INTO streamdetails( INSERT INTO streamdetails(
idFile, iStreamType, strVideoCodec, fVideoAspect, idFile, iStreamType, strVideoCodec, fVideoAspect,
iVideoWidth, iVideoHeight, iVideoDuration ,strStereoMode) iVideoWidth, iVideoHeight, iVideoDuration ,strStereoMode)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) '''
''' self.cursor.execute(query,
) (fileid, 0, videotrack['codec'],
self.cursor.execute(query, (fileid, 0, videotrack['codec'], videotrack['aspect'], videotrack['width'],
videotrack['aspect'], videotrack['width'], videotrack['height'], videotrack['height'], runtime,
runtime ,videotrack['video3DFormat'])) videotrack['video3DFormat']))
for audiotrack in streamdetails['audio']:
# Audio details query = '''
for audiotrack in streamdetails['audio']: INSERT INTO streamdetails(
query = ( idFile, iStreamType, strAudioCodec, iAudioChannels,
''' strAudioLanguage)
INSERT INTO streamdetails( VALUES (?, ?, ?, ?, ?)
idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) '''
self.cursor.execute(query,
VALUES (?, ?, ?, ?, ?) (fileid, 1, audiotrack['codec'],
''' audiotrack['channels'],
) audiotrack['language']))
self.cursor.execute(query, (fileid, 1, audiotrack['codec'], for subtitletrack in streamdetails['subtitle']:
audiotrack['channels'], audiotrack['language'])) query = '''
INSERT INTO streamdetails(idFile, iStreamType,
# Subtitles details strSubtitleLanguage)
for subtitletrack in streamdetails['subtitle']: VALUES (?, ?, ?)
query = ( '''
''' self.cursor.execute(query, (fileid, 2, subtitletrack))
INSERT INTO streamdetails(
idFile, iStreamType, strSubtitleLanguage)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (fileid, 2, subtitletrack))
def resume_points(self): def resume_points(self):
""" """
@ -722,26 +561,6 @@ class KodiDBMethods(object):
ids.append(row[0]) ids.append(row[0])
return ids return ids
def getUnplayedMusicItems(self):
"""
MUSIC
Returns all Kodi Item idFile that have not yet been completely played
"""
query = ' '.join((
"SELECT idSong",
"FROM song",
"WHERE iTimesPlayed = ?"
))
try:
rows = self.cursor.execute(query, (0, ))
except:
return []
ids = []
for row in rows:
ids.append(row[0])
return ids
def video_id_from_filename(self, filename, path): def video_id_from_filename(self, filename, path):
""" """
Returns the tuple (itemId, type) where Returns the tuple (itemId, type) where
@ -846,46 +665,6 @@ class KodiDBMethods(object):
return return
return song_id[0] return song_id[0]
def getUnplayedItems(self):
"""
VIDEOS
Returns all Kodi Item idFile that have not yet been completely played
"""
query = ' '.join((
"SELECT idFile",
"FROM files",
"WHERE playCount IS NULL OR playCount = ''"
))
try:
rows = self.cursor.execute(query)
except:
return []
ids = []
for row in rows:
ids.append(row[0])
return ids
def getVideoRuntime(self, kodiid, mediatype):
if mediatype == v.KODI_TYPE_MOVIE:
query = ' '.join((
"SELECT c11",
"FROM movie",
"WHERE idMovie = ?",
))
elif mediatype == v.KODI_TYPE_EPISODE:
query = ' '.join((
"SELECT c09",
"FROM episode",
"WHERE idEpisode = ?",
))
self.cursor.execute(query, (kodiid,))
try:
runtime = self.cursor.fetchone()[0]
except TypeError:
return None
return int(runtime)
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
@ -951,60 +730,6 @@ class KodiDBMethods(object):
""" """
self.cursor.execute('DELETE FROM bookmark where idFile = ?', (file_id,)) self.cursor.execute('DELETE FROM bookmark where idFile = ?', (file_id,))
def addTags(self, kodiid, tags, mediatype):
# First, delete any existing tags associated to the id
query = ' '.join((
"DELETE FROM tag_link",
"WHERE media_id = ?",
"AND media_type = ?"
))
self.cursor.execute(query, (kodiid, mediatype))
# Add tags
LOG.debug("Adding Tags: %s", tags)
for tag in tags:
self.addTag(kodiid, tag, mediatype)
def delete_tags(self, kodi_id, kodi_type):
"""
Removes the genre links as well as orphaned genres from the Kodi DB
"""
self._delete_from_link_and_table(kodi_id,
kodi_type,
'tag_link',
'tag',
'tag_id')
def addTag(self, kodiid, tag, mediatype):
query = ' '.join((
"SELECT tag_id",
"FROM tag",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (tag,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
# Create the tag, because it does not exist
tag_id = self.createTag(tag)
LOG.debug("Adding tag: %s", tag)
finally:
# Assign tag to item
query = (
'''
INSERT OR REPLACE INTO tag_link(
tag_id, media_id, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (tag_id, kodiid, mediatype))
def createTag(self, name): def createTag(self, name):
# This will create and return the tag_id # This will create and return the tag_id
query = ' '.join(( query = ' '.join((