This commit is contained in:
Croneter 2018-06-15 15:15:35 +02:00
parent 51444111d2
commit 0220c84554
4 changed files with 148 additions and 145 deletions

View file

@ -117,12 +117,12 @@ class Items(object):
if kodi_type == v.KODI_TYPE_MOVIE: if kodi_type == v.KODI_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)
setid = self.kodi_db.createBoxset(setname) setid = self.kodi_db.create_collection(setname)
self.artwork.modify_artwork(api.set_artwork(), self.artwork.modify_artwork(api.set_artwork(),
setid, setid,
v.KODI_TYPE_SET, v.KODI_TYPE_SET,
self.kodicursor) self.kodicursor)
self.kodi_db.assignBoxset(setid, kodi_id) self.kodi_db.assign_collection(setid, kodi_id)
return True return True
def updateUserdata(self, xml): def updateUserdata(self, xml):
@ -143,7 +143,7 @@ class Items(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.addPlaystate(fileid, self.kodi_db.set_resume(fileid,
userdata['Resume'], userdata['Resume'],
userdata['Runtime'], userdata['Runtime'],
userdata['PlayCount'], userdata['PlayCount'],
@ -171,7 +171,7 @@ class Items(object):
view_count = 1 view_count = 1
resume = 0 resume = 0
# Do the actual update # Do the actual update
self.kodi_db.addPlaystate(file_id, self.kodi_db.set_resume(file_id,
resume, resume,
duration, duration,
view_count, view_count,
@ -448,9 +448,9 @@ class Movies(Items):
tags.append("Favorite movies") tags.append("Favorite movies")
self.kodi_db.modify_tags(movieid, v.KODI_TYPE_MOVIE, tags) self.kodi_db.modify_tags(movieid, v.KODI_TYPE_MOVIE, tags)
# Add any sets from Plex collection tags # Add any sets from Plex collection tags
self.kodi_db.addSets(movieid, collections, kodicursor) self.kodi_db.add_sets(movieid, collections)
# Process playstates # Process playstates
self.kodi_db.addPlaystate(fileid, self.kodi_db.set_resume(fileid,
resume, resume,
runtime, runtime,
playcount, playcount,
@ -993,7 +993,7 @@ class TVShows(Items):
kodicursor) kodicursor)
streams = api.mediastreams() streams = api.mediastreams()
self.kodi_db.modify_streams(fileid, streams, runtime) self.kodi_db.modify_streams(fileid, streams, runtime)
self.kodi_db.addPlaystate(fileid, self.kodi_db.set_resume(fileid,
resume, resume,
runtime, runtime,
playcount, playcount,
@ -1009,7 +1009,7 @@ class TVShows(Items):
filename)) filename))
pathid = self.kodi_db.add_video_path(path) pathid = self.kodi_db.add_video_path(path)
fileid = self.kodi_db.add_file(filename, pathid, dateadded) fileid = self.kodi_db.add_file(filename, pathid, dateadded)
self.kodi_db.addPlaystate(fileid, self.kodi_db.set_resume(fileid,
resume, resume,
runtime, runtime,
playcount, playcount,
@ -1201,7 +1201,7 @@ class Music(Items):
# 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.
artistid = self.kodi_db.addArtist(name, musicBrainzId) artistid = self.kodi_db.add_artist(name, musicBrainzId)
# Create the reference in plex table # Create the reference in plex table
plex_db.addReference(itemid, plex_db.addReference(itemid,
v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_ARTIST,
@ -1304,7 +1304,7 @@ class Music(Items):
# 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.
album_id = self.kodi_db.addAlbum(name, musicBrainzId) album_id = self.kodi_db.add_album(name, musicBrainzId)
# Create the reference in plex table # Create the reference in plex table
plex_db.addReference(plex_id, plex_db.addReference(plex_id,
v.PLEX_TYPE_ALBUM, v.PLEX_TYPE_ALBUM,
@ -1389,7 +1389,9 @@ class Music(Items):
''' '''
kodicursor.execute(query, (artist_id, name, year)) kodicursor.execute(query, (artist_id, name, year))
if v.KODIVERSION < 18: if v.KODIVERSION < 18:
self.kodi_db.addMusicGenres(album_id, self.genres, v.KODI_TYPE_ALBUM) self.kodi_db.add_music_genres(album_id,
self.genres,
v.KODI_TYPE_ALBUM)
# Update artwork # Update artwork
artwork.modify_artwork(artworks, album_id, v.KODI_TYPE_ALBUM, kodicursor) artwork.modify_artwork(artworks, album_id, v.KODI_TYPE_ALBUM, kodicursor)
# Add all children - all tracks # Add all children - all tracks
@ -1541,7 +1543,7 @@ class Music(Items):
LOG.info("ADD song itemid: %s - Title: %s", itemid, title) LOG.info("ADD song itemid: %s - Title: %s", itemid, title)
# Add path # Add path
pathid = self.kodi_db.add_music_path(path, strHash="123") pathid = self.kodi_db.add_music_path(path, hash_string="123")
try: try:
# Get the album # Get the album
@ -1553,7 +1555,7 @@ class Music(Items):
if album_name: if album_name:
LOG.info("Creating virtual music album for song: %s.", LOG.info("Creating virtual music album for song: %s.",
itemid) itemid)
albumid = self.kodi_db.addAlbum( albumid = self.kodi_db.add_album(
album_name, album_name,
api.provider('MusicBrainzAlbum')) api.provider('MusicBrainzAlbum'))
plex_db.addReference("%salbum%s" % (itemid, albumid), plex_db.addReference("%salbum%s" % (itemid, albumid),
@ -1711,7 +1713,7 @@ class Music(Items):
artist_name)) artist_name))
# Add genres # Add genres
if genres: if genres:
self.kodi_db.addMusicGenres(songid, genres, v.KODI_TYPE_SONG) self.kodi_db.add_music_genres(songid, genres, v.KODI_TYPE_SONG)
artworks = api.artwork() artworks = api.artwork()
artwork.modify_artwork(artworks, songid, v.KODI_TYPE_SONG, kodicursor) artwork.modify_artwork(artworks, songid, v.KODI_TYPE_SONG, kodicursor)
if item.get('parentKey') is None: if item.get('parentKey') is None:

View file

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# Connect to the Kodi databases (video and music) and operate on them
#
############################################################################### ###############################################################################
from logging import getLogger from logging import getLogger
from ntpath import dirname from ntpath import dirname
@ -156,7 +158,12 @@ class KodiDBMethods(object):
content, scraper, 1)) content, scraper, 1))
return pathid return pathid
def add_music_path(self, path, strHash=None): 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 # SQL won't return existing paths otherwise
if path is None: if path is None:
path = '' path = ''
@ -171,7 +178,7 @@ class KodiDBMethods(object):
INSERT INTO path(idPath, strPath, strHash) INSERT INTO path(idPath, strPath, strHash)
VALUES (?, ?, ?) VALUES (?, ?, ?)
''' '''
self.cursor.execute(query, (pathid, path, strHash)) self.cursor.execute(query, (pathid, path, hash_string))
return pathid return pathid
def get_path(self, path): def get_path(self, path):
@ -597,21 +604,13 @@ class KodiDBMethods(object):
Returns None if not found OR if too many entries were found Returns None if not found OR if too many entries were found
""" """
query = ' '.join(( query = 'SELECT idFile, idPath FROM files WHERE strFilename = ?'
"SELECT idFile, idPath",
"FROM files",
"WHERE strFilename = ?"
))
self.cursor.execute(query, (filename,)) self.cursor.execute(query, (filename,))
files = self.cursor.fetchall() files = self.cursor.fetchall()
if len(files) == 0: if len(files) == 0:
LOG.info('Did not find any file, abort') LOG.info('Did not find any file, abort')
return return
query = ' '.join(( query = 'SELECT strPath FROM path WHERE idPath = ?'
"SELECT strPath",
"FROM path",
"WHERE idPath = ?"
))
# result will contain a list of all idFile with matching filename and # result will contain a list of all idFile with matching filename and
# matching path # matching path
result = [] result = []
@ -619,14 +618,14 @@ class KodiDBMethods(object):
# Use idPath to get path as a string # Use idPath to get path as a string
self.cursor.execute(query, (file[1],)) self.cursor.execute(query, (file[1],))
try: try:
strPath = self.cursor.fetchone()[0] path_str = self.cursor.fetchone()[0]
except TypeError: except TypeError:
# idPath not found; skip # idPath not found; skip
continue continue
# For whatever reason, double might have become triple # For whatever reason, double might have become triple
strPath = strPath.replace('///', '//') path_str = path_str.replace('///', '//')
strPath = strPath.replace('\\\\\\', '\\\\') path_str = path_str.replace('\\\\\\', '\\\\')
if strPath == path: if path_str == path:
result.append(file[0]) result.append(file[0])
if len(result) == 0: if len(result) == 0:
LOG.info('Did not find matching paths, abort') LOG.info('Did not find matching paths, abort')
@ -637,33 +636,26 @@ class KodiDBMethods(object):
LOG.warn('We found too many items with matching filenames and ' LOG.warn('We found too many items with matching filenames and '
' paths, aborting') ' paths, aborting')
return return
idFile = result[0] file_id = result[0]
# Try movies first # Try movies first
query = ' '.join(( self.cursor.execute('SELECT idMovie FROM movie WHERE idFile = ?',
"SELECT idMovie", (file_id, ))
"FROM movie",
"WHERE idFile = ?"
))
self.cursor.execute(query, (idFile,))
try: try:
itemId = self.cursor.fetchone()[0] movie_id = self.cursor.fetchone()[0]
typus = v.KODI_TYPE_MOVIE typus = v.KODI_TYPE_MOVIE
except TypeError: except TypeError:
# Try tv shows next # Try tv shows next
query = ' '.join(( query = 'SELECT idEpisode FROM episode WHERE idFile = ?'
"SELECT idEpisode", self.cursor.execute(query,
"FROM episode", (file_id, ))
"WHERE idFile = ?"
))
self.cursor.execute(query, (idFile,))
try: try:
itemId = self.cursor.fetchone()[0] movie_id = self.cursor.fetchone()[0]
typus = v.KODI_TYPE_EPISODE typus = v.KODI_TYPE_EPISODE
except TypeError: except TypeError:
LOG.warn('Unexpectantly did not find a match!') LOG.warn('Unexpectantly did not find a match!')
return return
return itemId, typus return movie_id, typus
def music_id_from_filename(self, filename, path): def music_id_from_filename(self, filename, path):
""" """
@ -726,8 +718,12 @@ class KodiDBMethods(object):
answ = None answ = None
return answ return answ
def addPlaystate(self, file_id, resume_seconds, total_seconds, playcount, def set_resume(self, file_id, resume_seconds, total_seconds, playcount,
dateplayed, plex_type): dateplayed, plex_type):
"""
Adds a resume marker for a video library item. Will even set 2,
considering add-on path widget hacks.
"""
if not state.DIRECT_PATHS and plex_type == v.PLEX_TYPE_EPISODE: if not state.DIRECT_PATHS and plex_type == v.PLEX_TYPE_EPISODE:
# Need to make sure to set a SECOND bookmark entry for another, # Need to make sure to set a SECOND bookmark entry for another,
# second file_id that points to the path .tvshows instead of # second file_id that points to the path .tvshows instead of
@ -746,7 +742,7 @@ class KodiDBMethods(object):
len(file_ids)) len(file_ids))
raise RuntimeError raise RuntimeError
for new_id in file_ids: for new_id in file_ids:
self.addPlaystate(new_id[0], resume_seconds, total_seconds, self.set_resume(new_id[0], resume_seconds, total_seconds,
playcount, dateplayed, None) playcount, dateplayed, None)
return return
@ -779,87 +775,72 @@ class KodiDBMethods(object):
'', '',
1)) 1))
def createTag(self, name): def create_tag(self, name):
# This will create and return the tag_id """
query = ' '.join(( Will create a new tag if needed and return the tag_id
"""
"SELECT tag_id", query = '''
"FROM tag", SELECT tag_id FROM tag WHERE name = ? COLLATE NOCASE
"WHERE name = ?", '''
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,)) self.cursor.execute(query, (name,))
try: try:
tag_id = self.cursor.fetchone()[0] tag_id = self.cursor.fetchone()[0]
except TypeError: except TypeError:
self.cursor.execute("select coalesce(max(tag_id),0) from tag") self.cursor.execute("select coalesce(max(tag_id),0) from tag")
tag_id = self.cursor.fetchone()[0] + 1 tag_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO tag(tag_id, name) values(?, ?)" query = "INSERT INTO tag(tag_id, name) values(?, ?)"
self.cursor.execute(query, (tag_id, name)) self.cursor.execute(query, (tag_id, name))
LOG.debug("Create tag_id: %s name: %s", tag_id, name) LOG.debug("Create tag_id: %s name: %s", tag_id, name)
return tag_id return tag_id
def updateTag(self, oldtag, newtag, kodiid, mediatype): def update_tag(self, oldtag, newtag, kodiid, mediatype):
"""
Updates the tag_id by replaying oldtag with newtag
"""
query = '''
UPDATE tag_link SET tag_id = ?
WHERE media_id = ? AND media_type = ? AND tag_id = ?
'''
try: try:
query = ' '.join((
"UPDATE tag_link",
"SET tag_id = ?",
"WHERE media_id = ?",
"AND media_type = ?",
"AND tag_id = ?"
))
self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,)) self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
except Exception as e: except:
# The new tag we are going to apply already exists for this item # The new tag we are going to apply already exists for this item
# delete current tag instead # delete current tag instead
query = ' '.join(( query = '''
DELETE FROM tag_link
"DELETE FROM tag_link", WHERE media_id = ? AND media_type = ? AND tag_id = ?
"WHERE media_id = ?", '''
"AND media_type = ?",
"AND tag_id = ?"
))
self.cursor.execute(query, (kodiid, mediatype, oldtag,)) self.cursor.execute(query, (kodiid, mediatype, oldtag,))
def addSets(self, movieid, collections, kodicursor): def add_sets(self, movieid, collections):
"""
Will add the movie to all collections (a list of unicodes)
"""
for setname in collections: for setname in collections:
setid = self.createBoxset(setname) setid = self.create_collection(setname)
self.assignBoxset(setid, movieid) self.assign_collection(setid, movieid)
def createBoxset(self, boxsetname): def create_collection(self, set_name):
"""
LOG.debug("Adding boxset: %s", boxsetname) Returns the collection/set id for set_name [unicode]
query = ' '.join(( """
LOG.debug("Adding boxset: %s", set_name)
"SELECT idSet", query = 'SELECT idSet FROM sets WHERE strSet = ? COLLATE NOCASE'
"FROM sets", self.cursor.execute(query, (set_name,))
"WHERE strSet = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (boxsetname,))
try: try:
setid = self.cursor.fetchone()[0] setid = self.cursor.fetchone()[0]
except TypeError: except TypeError:
self.cursor.execute("select coalesce(max(idSet),0) from sets") self.cursor.execute("select coalesce(max(idSet),0) from sets")
setid = self.cursor.fetchone()[0] + 1 setid = self.cursor.fetchone()[0] + 1
query = "INSERT INTO sets(idSet, strSet) values(?, ?)" query = "INSERT INTO sets(idSet, strSet) values(?, ?)"
self.cursor.execute(query, (setid, boxsetname)) self.cursor.execute(query, (setid, set_name))
return setid return setid
def assignBoxset(self, setid, movieid): def assign_collection(self, setid, movieid):
"""
query = ' '.join(( Assign the movie to one set/collection
"""
"UPDATE movie", query = 'UPDATE movie SET idSet = ? WHERE idMovie = ?'
"SET idSet = ?",
"WHERE idMovie = ?"
))
self.cursor.execute(query, (setid, movieid,)) self.cursor.execute(query, (setid, movieid,))
def remove_from_set(self, movieid): def remove_from_set(self, movieid):
@ -912,7 +893,10 @@ class KodiDBMethods(object):
self.cursor.execute(query, (seasonid, showid, seasonnumber)) self.cursor.execute(query, (seasonid, showid, seasonnumber))
return seasonid return seasonid
def addArtist(self, name, musicbrainz): def add_artist(self, name, musicbrainz):
"""
Adds a single artist's name to the db
"""
query = ''' query = '''
SELECT idArtist, strArtist SELECT idArtist, strArtist
FROM artist FROM artist
@ -1047,8 +1031,10 @@ class KodiDBMethods(object):
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?', self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
(genre[0], )) (genre[0], ))
def add_album(self, name, musicbrainz):
def addAlbum(self, name, musicbrainz): """
Adds a single album to the DB
"""
query = 'SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?' query = 'SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?'
self.cursor.execute(query, (musicbrainz,)) self.cursor.execute(query, (musicbrainz,))
try: try:
@ -1065,7 +1051,10 @@ class KodiDBMethods(object):
self.cursor.execute(query, (albumid, name, musicbrainz, 'album')) self.cursor.execute(query, (albumid, name, musicbrainz, 'album'))
return albumid return albumid
def addMusicGenres(self, kodiid, genres, mediatype): def add_music_genres(self, kodiid, genres, mediatype):
"""
Adds a list of genres (list of unicode) for a certain Kodi item
"""
if mediatype == "album": if mediatype == "album":
# Delete current genres for clean slate # Delete current genres for clean slate
query = 'DELETE FROM album_genre WHERE idAlbum = ?' query = 'DELETE FROM album_genre WHERE idAlbum = ?'
@ -1121,13 +1110,13 @@ class KodiDBMethods(object):
Updates userrating for >=Krypton Updates userrating for >=Krypton
""" """
if kodi_type == v.KODI_TYPE_MOVIE: if kodi_type == v.KODI_TYPE_MOVIE:
ID = 'idMovie' identifier = 'idMovie'
elif kodi_type == v.KODI_TYPE_EPISODE: elif kodi_type == v.KODI_TYPE_EPISODE:
ID = 'idEpisode' identifier = 'idEpisode'
elif kodi_type == v.KODI_TYPE_SONG: elif kodi_type == v.KODI_TYPE_SONG:
ID = 'idSong' identifier = 'idSong'
query = '''UPDATE %s SET userrating = ? WHERE ? = ?''' % kodi_type query = '''UPDATE %s SET userrating = ? WHERE ? = ?''' % kodi_type
self.cursor.execute(query, (userrating, ID, kodi_id)) self.cursor.execute(query, (userrating, identifier, kodi_id))
def add_uniqueid(self, *args): def add_uniqueid(self, *args):
""" """
@ -1146,6 +1135,9 @@ class KodiDBMethods(object):
self.cursor.execute(query, (args)) self.cursor.execute(query, (args))
def get_uniqueid(self, kodi_id, kodi_type): def get_uniqueid(self, kodi_id, kodi_type):
"""
Returns the uniqueid_id
"""
query = ''' query = '''
SELECT uniqueid_id FROM uniqueid SELECT uniqueid_id FROM uniqueid
WHERE media_id = ? AND media_type = ? WHERE media_id = ? AND media_type = ?
@ -1171,6 +1163,9 @@ class KodiDBMethods(object):
self.cursor.execute(query, (args)) self.cursor.execute(query, (args))
def remove_uniqueid(self, kodi_id, kodi_type): def remove_uniqueid(self, kodi_id, kodi_type):
"""
Deletes the entry from the uniqueid table for the item
"""
query = ''' query = '''
DELETE FROM uniqueid DELETE FROM uniqueid
WHERE media_id = ? AND media_type = ? WHERE media_id = ? AND media_type = ?
@ -1178,6 +1173,9 @@ class KodiDBMethods(object):
self.cursor.execute(query, (kodi_id, kodi_type)) self.cursor.execute(query, (kodi_id, kodi_type))
def get_ratingid(self, kodi_id, kodi_type): def get_ratingid(self, kodi_id, kodi_type):
"""
Create if needed and return the unique rating_id from rating table
"""
query = ''' query = '''
SELECT rating_id FROM rating SELECT rating_id FROM rating
WHERE media_id = ? AND media_type = ? WHERE media_id = ? AND media_type = ?
@ -1220,6 +1218,9 @@ class KodiDBMethods(object):
self.cursor.execute(query, (args)) self.cursor.execute(query, (args))
def remove_ratings(self, kodi_id, kodi_type): def remove_ratings(self, kodi_id, kodi_type):
"""
Removes all ratings from the rating table for the item
"""
query = ''' query = '''
DELETE FROM rating DELETE FROM rating
WHERE media_id = ? AND media_type = ? WHERE media_id = ? AND media_type = ?

View file

@ -519,7 +519,7 @@ def _record_playstate(status, ended):
playcount += 1 playcount += 1
time = 0 time = 0
with kodidb.GetKodiDB('video') as kodi_db: with kodidb.GetKodiDB('video') as kodi_db:
kodi_db.addPlaystate(kodi_db_item[1], kodi_db.set_resume(kodi_db_item[1],
time, time,
totaltime, totaltime,
playcount, playcount,

View file

@ -315,7 +315,7 @@ class LibrarySync(Thread):
current_tagid = view[2] current_tagid = view[2]
except TypeError: except TypeError:
LOG.info('Creating viewid: %s in Plex database.', folderid) LOG.info('Creating viewid: %s in Plex database.', folderid)
tagid = kodi_db.createTag(foldername) tagid = kodi_db.create_tag(foldername)
# Create playlist for the video library # Create playlist for the video library
if (foldername not in playlists and if (foldername not in playlists and
mediatype in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)): mediatype in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
@ -350,7 +350,7 @@ class LibrarySync(Thread):
# View was modified, update with latest info # View was modified, update with latest info
if current_viewname != foldername: if current_viewname != foldername:
LOG.info('viewid: %s new viewname: %s', folderid, foldername) LOG.info('viewid: %s new viewname: %s', folderid, foldername)
tagid = kodi_db.createTag(foldername) tagid = kodi_db.create_tag(foldername)
# Update view with new info # Update view with new info
plex_db.updateView(foldername, tagid, folderid) plex_db.updateView(foldername, tagid, folderid)
@ -396,7 +396,7 @@ class LibrarySync(Thread):
items = plex_db.getItem_byView(folderid) items = plex_db.getItem_byView(folderid)
for item in items: for item in items:
# Remove the "s" from viewtype for tags # Remove the "s" from viewtype for tags
kodi_db.updateTag( kodi_db.update_tag(
current_tagid, tagid, item[0], current_viewtype[:-1]) current_tagid, tagid, item[0], current_viewtype[:-1])
else: else:
# Validate the playlist exists or recreate it # Validate the playlist exists or recreate it