247 lines
10 KiB
Python
247 lines
10 KiB
Python
#!/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
|
|
|
|
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_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
|