444 lines
No EOL
21 KiB
Python
444 lines
No EOL
21 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
#################################################################################################
|
|
# WriteKodiMusicDB
|
|
#################################################################################################
|
|
|
|
import sqlite3
|
|
from datetime import datetime
|
|
from ntpath import split as ntsplit
|
|
|
|
import xbmc
|
|
import xbmcgui
|
|
import xbmcaddon
|
|
|
|
from ClientInformation import ClientInformation
|
|
import Utils as utils
|
|
from API import API
|
|
from PlayUtils import PlayUtils
|
|
from ReadKodiDB import ReadKodiDB
|
|
from ReadEmbyDB import ReadEmbyDB
|
|
from TextureCache import TextureCache
|
|
|
|
|
|
class WriteKodiMusicDB():
|
|
|
|
textureCache = TextureCache()
|
|
kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
|
|
|
addon = xbmcaddon.Addon()
|
|
addonName = ClientInformation().getAddonName()
|
|
WINDOW = xbmcgui.Window(10000)
|
|
|
|
username = WINDOW.getProperty('currUser')
|
|
userid = WINDOW.getProperty('userId%s' % username)
|
|
server = WINDOW.getProperty('server%s' % username)
|
|
|
|
directpath = addon.getSetting('useDirectPaths') == "true"
|
|
|
|
def logMsg(self, msg, lvl = 1):
|
|
|
|
className = self.__class__.__name__
|
|
utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
|
|
|
|
def addOrUpdateArtistToKodiLibrary(self, embyId, connection, cursor):
|
|
|
|
MBitem = ReadEmbyDB().getFullItem(embyId)
|
|
|
|
# If the item already exist in the local Kodi DB we'll perform a full item update
|
|
# If the item doesn't exist, we'll add it to the database
|
|
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (embyId,))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except:
|
|
artistid = None
|
|
|
|
##### The artist details #####
|
|
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
dateadded = API().getDateCreated(MBitem)
|
|
checksum = API().getChecksum(MBitem)
|
|
|
|
name = MBitem['Name']
|
|
musicBrainzId = API().getProvider(MBitem, "musicBrainzArtist")
|
|
genres = " / ".join(MBitem.get('Genres'))
|
|
bio = API().getOverview(MBitem)
|
|
|
|
# Associate artwork
|
|
thumb = API().getArtwork(MBitem, "Primary")
|
|
if thumb:
|
|
thumb = "<thumb>%s</thumb>" % thumb
|
|
fanart = API().getArtwork(MBitem, "Backdrop")
|
|
if fanart:
|
|
fanart = "<fanart>%s</fanart>" % fanart
|
|
|
|
# Safety check 1: does the artist already exist?
|
|
cursor.execute("SELECT idArtist FROM artist WHERE strArtist = ?", (name,))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except: pass
|
|
|
|
# Safety check 2: does the MusicBrainzArtistId already exist?
|
|
cursor.execute("SELECT idArtist FROM artist WHERE strMusicBrainzArtistID = ?", (musicBrainzId,))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except: pass
|
|
|
|
|
|
##### UPDATE THE ARTIST #####
|
|
if artistid:
|
|
self.logMsg("UPDATE artist to Kodi library, Id: %s - Artist: %s" % (embyId, name), 1)
|
|
|
|
query = "UPDATE artist SET strArtist = ?, strMusicBrainzArtistID = ?, strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?, dateadded = ? WHERE idArtist = ?"
|
|
cursor.execute(query, (name, musicBrainzId, genres, bio, thumb, fanart, lastScraped, dateadded, artistid))
|
|
|
|
# Update the checksum in emby table
|
|
query = "UPDATE emby SET checksum = ? WHERE emby_id = ?"
|
|
cursor.execute(query, (checksum, embyId))
|
|
|
|
##### OR ADD THE ARTIST #####
|
|
else:
|
|
self.logMsg("ADD artist to Kodi library, Id: %s - Artist: %s" % (embyId, name), 1)
|
|
|
|
# Create the artist
|
|
cursor.execute("select coalesce(max(idArtist),0) as artistid from artist")
|
|
artistid = cursor.fetchone()[0] + 1
|
|
query = "INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID, strGenres, strBiography, strImage, strFanart, lastScraped, dateAdded) values(?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (artistid, name, musicBrainzId, genres, bio, thumb, fanart, lastScraped, dateadded))
|
|
|
|
# Create the reference in emby table
|
|
query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)"
|
|
cursor.execute(query, (embyId, artistid, "artist", checksum))
|
|
|
|
|
|
# Update artwork
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), artistid, "artist", "thumb", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), artistid, "artist", "poster", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), artistid, "artist", "banner", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), artistid, "artist", "clearlogo", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), artistid, "artist", "clearart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), artistid, "artist", "landscape", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), artistid, "artist", "discart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), artistid, "artist", "fanart", cursor)
|
|
|
|
def addOrUpdateAlbumToKodiLibrary(self, embyId, connection, cursor):
|
|
|
|
kodiVersion = self.kodiversion
|
|
|
|
MBitem = ReadEmbyDB().getFullItem(embyId)
|
|
|
|
# If the item already exist in the local Kodi DB we'll perform a full item update
|
|
# If the item doesn't exist, we'll add it to the database
|
|
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (embyId,))
|
|
try:
|
|
albumid = cursor.fetchone()[0]
|
|
except:
|
|
albumid = None
|
|
|
|
genres = MBitem.get('Genres')
|
|
##### The album details #####
|
|
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
dateadded = API().getDateCreated(MBitem)
|
|
checksum = API().getChecksum(MBitem)
|
|
|
|
name = MBitem['Name']
|
|
musicBrainzId = API().getProvider(MBitem, "musicBrainzAlbum")
|
|
year = MBitem.get('ProductionYear')
|
|
genre = " / ".join(genres)
|
|
bio = API().getOverview(MBitem)
|
|
|
|
MBartists = []
|
|
for item in MBitem['AlbumArtists']:
|
|
MBartists.append(item['Name'])
|
|
artists = " / ".join(MBartists)
|
|
|
|
# Associate the artwork
|
|
thumb = API().getArtwork(MBitem, "Primary")
|
|
if thumb:
|
|
thumb = "<thumb>%s</thumb>" % thumb
|
|
|
|
|
|
##### UPDATE THE ALBUM #####
|
|
if albumid:
|
|
self.logMsg("UPDATE album to Kodi library, Id: %s - Title: %s" % (embyId, name), 1)
|
|
|
|
if kodiVersion == 15:
|
|
# Kodi Isengard
|
|
query = "UPDATE album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ? WHERE idAlbum = ?"
|
|
cursor.execute(query, (name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, "album", albumid))
|
|
else:
|
|
# Kodi Gotham and Helix
|
|
query = "UPDATE album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?, lastScraped = ?, dateAdded = ? WHERE idAlbum = ?"
|
|
cursor.execute(query, (name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, albumid))
|
|
|
|
# Update the checksum in emby table
|
|
query = "UPDATE emby SET checksum = ? WHERE emby_id = ?"
|
|
cursor.execute(query, (checksum, embyId))
|
|
|
|
##### OR ADD THE ALBUM #####
|
|
else:
|
|
self.logMsg("ADD album to Kodi library, Id: %s - Title: %s" % (embyId, name), 1)
|
|
|
|
# Safety check: does the strMusicBrainzAlbumID already exist?
|
|
cursor.execute("SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?", (musicBrainzId,))
|
|
try:
|
|
albumid = cursor.fetchone()[0]
|
|
except:
|
|
# Create the album
|
|
cursor.execute("select coalesce(max(idAlbum),0) as albumid from album")
|
|
albumid = cursor.fetchone()[0] + 1
|
|
if kodiVersion == 15:
|
|
# Kodi Isengard
|
|
query = "INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, strReview, strImage, lastScraped, dateAdded, strReleaseType) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (albumid, name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, "album"))
|
|
else:
|
|
# Kodi Gotham and Helix
|
|
query = "INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, strReview, strImage, lastScraped, dateAdded) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (albumid, name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded))
|
|
|
|
# Create the reference in emby table
|
|
query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)"
|
|
cursor.execute(query, (embyId, albumid, "album", checksum))
|
|
|
|
|
|
# Add genres
|
|
self.AddGenresToMedia(albumid, genres, "album", cursor)
|
|
|
|
# Update artwork
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), albumid, "album", "thumb", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "BoxRear"), albumid, "album", "poster", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), albumid, "album", "banner", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), albumid, "album", "clearlogo", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), albumid, "album", "clearart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), albumid, "album", "landscape", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), albumid, "album", "discart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), albumid, "album", "fanart", cursor)
|
|
|
|
# Link album to artists
|
|
if MBartists:
|
|
album_artists = MBitem['AlbumArtists']
|
|
elif MBitem.get('ArtistItems'):
|
|
album_artists = MBitem['ArtistItems']
|
|
|
|
for artist in album_artists:
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except: pass
|
|
else:
|
|
query = "INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist) values(?, ?, ?)"
|
|
cursor.execute(query, (artistid, albumid, artist['Name']))
|
|
# Update discography
|
|
query = "INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear) values(?, ?, ?)"
|
|
cursor.execute(query, (artistid, name, str(year)))
|
|
|
|
def addOrUpdateSongToKodiLibrary(self, embyId, connection, cursor):
|
|
|
|
kodiVersion = self.kodiversion
|
|
|
|
MBitem = ReadEmbyDB().getFullItem(embyId)
|
|
|
|
# If the item already exist in the local Kodi DB we'll perform a full item update
|
|
# If the item doesn't exist, we'll add it to the database
|
|
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (embyId,))
|
|
try:
|
|
songid = cursor.fetchone()[0]
|
|
except:
|
|
songid = None
|
|
|
|
timeInfo = API().getTimeInfo(MBitem)
|
|
userData = API().getUserData(MBitem)
|
|
genres = MBitem.get('Genres')
|
|
|
|
##### The song details #####
|
|
playcount = userData.get('PlayCount')
|
|
lastplayed = userData.get('LastPlayedDate')
|
|
dateadded = API().getDateCreated(MBitem)
|
|
checksum = API().getChecksum(MBitem)
|
|
|
|
name = MBitem['Name']
|
|
musicBrainzId = API().getProvider(MBitem, "musicBrainzTrackId")
|
|
genre = " / ".join(genres)
|
|
artists = " / ".join(MBitem.get('Artists'))
|
|
track = MBitem.get('IndexNumber')
|
|
year = MBitem.get('ProductionYear')
|
|
bio = API().getOverview(MBitem)
|
|
duration = timeInfo.get('TotalTime')
|
|
|
|
# Get the path and filename
|
|
playurl = PlayUtils().directPlay(MBitem)
|
|
try:
|
|
path, filename = ntsplit(playurl)
|
|
if "\\" in playurl:
|
|
path = "%s\\" % path
|
|
elif "/" in playurl:
|
|
path = "%s/" % path
|
|
except: # playurl returned false - using server streaming path, because could not figure out plugin paths for music DB
|
|
playurl = PlayUtils().directstream(MBitem, self.server, embyId, "Audio")
|
|
filename = "stream.mp3"
|
|
path = playurl.replace(filename, "")
|
|
|
|
|
|
# Validate the path in database
|
|
cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?", (path,))
|
|
try:
|
|
pathid = cursor.fetchone()[0]
|
|
except:
|
|
cursor.execute("select coalesce(max(idPath),0) as pathid from path")
|
|
pathid = cursor.fetchone()[0] + 1
|
|
query = "INSERT INTO path(idPath, strPath) values(?, ?)"
|
|
cursor.execute(query, (pathid, path))
|
|
|
|
# Get the album
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (MBitem.get("AlbumId"),))
|
|
try:
|
|
albumid = cursor.fetchone()[0]
|
|
except:
|
|
# No album found, create a single's album
|
|
cursor.execute("select coalesce(max(idAlbum),0) as albumid from album")
|
|
albumid = cursor.fetchone()[0] + 1
|
|
if kodiVersion == 15:
|
|
# Kodi Isengard
|
|
query = "INSERT INTO album(idAlbum, strArtists, strGenres, iYear, dateAdded, strReleaseType) values(?, ?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (albumid, artists, genre, year, dateadded, "single"))
|
|
else:
|
|
# Kodi Gotham and Helix
|
|
query = "INSERT INTO album(idAlbum, strArtists, strGenres, iYear, dateAdded) values(?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (albumid, artists, genre, year, dateadded))
|
|
|
|
# Link album to artists
|
|
for artist in MBitem['ArtistItems']:
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except: pass
|
|
else:
|
|
query = "INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist) values(?, ?, ?)"
|
|
cursor.execute(query, (artistid, albumid, artist['Name']))
|
|
|
|
|
|
##### UPDATE THE SONG #####
|
|
if songid:
|
|
self.logMsg("UPDATE song to Kodi library, Id: %s - Title: %s" % (embyId, name), 1)
|
|
|
|
query = "UPDATE song SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?, iDuration = ?, iYear = ?, strFilename = ?, strMusicBrainzTrackID = ?, iTimesPlayed = ?, lastplayed = ? WHERE idSong = ?"
|
|
cursor.execute(query, (albumid, artists, genre, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed, songid))
|
|
|
|
# Update the checksum in emby table
|
|
query = "UPDATE emby SET checksum = ? WHERE emby_id = ?"
|
|
cursor.execute(query, (checksum, embyId))
|
|
|
|
##### OR ADD THE SONG #####
|
|
else:
|
|
self.logMsg("ADD song to Kodi library, Id: %s - Title: %s" % (embyId, name), 1)
|
|
|
|
# Create the song
|
|
cursor.execute("select coalesce(max(idSong),0) as songid from song")
|
|
songid = cursor.fetchone()[0] + 1
|
|
query = "INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack, iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (songid, albumid, pathid, artists, genre, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed))
|
|
|
|
# Create the reference in emby table
|
|
query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)"
|
|
cursor.execute(query, (embyId, songid, "song", checksum))
|
|
|
|
|
|
# Add genres
|
|
self.AddGenresToMedia(songid, genres, "song", cursor)
|
|
|
|
# Link song to album
|
|
if albumid:
|
|
query = "INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration) values(?, ?, ?, ?, ?)"
|
|
cursor.execute(query, (songid, albumid, track, name, duration))
|
|
|
|
# Link song to artist
|
|
for artist in MBitem.get('ArtistItems'):
|
|
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],))
|
|
try:
|
|
artistid = cursor.fetchone()[0]
|
|
except: pass
|
|
else:
|
|
query = "INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist) values(?, ?, ?)"
|
|
cursor.execute(query, (artistid, songid, artist['Name']))
|
|
|
|
# Update artwork
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), songid, "song", "thumb", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), songid, "song", "poster", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), songid, "song", "banner", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), songid, "song", "clearlogo", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), songid, "song", "clearart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), songid, "song", "landscape", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), songid, "song", "discart", cursor)
|
|
self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), songid, "song", "fanart", cursor)
|
|
|
|
def deleteItemFromKodiLibrary(self, id, connection, cursor):
|
|
|
|
cursor.execute("SELECT kodi_id, media_type FROM emby WHERE emby_id=?", (id,))
|
|
try:
|
|
result = cursor.fetchone()
|
|
kodi_id = result[0]
|
|
media_type = result[1]
|
|
except: pass
|
|
else:
|
|
if "artist" in media_type:
|
|
self.logMsg("Deleting artist from Kodi library, Id: %s" % id, 1)
|
|
cursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodi_id,))
|
|
elif "song" in media_type:
|
|
self.logMsg("Deleting song from Kodi library, Id: %s" % id, 1)
|
|
cursor.execute("DELETE FROM song WHERE idSong = ?", (kodi_id,))
|
|
elif "album" in media_type:
|
|
self.logMsg("Deleting album from Kodi library, Id: %s" % id, 1)
|
|
cursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodi_id,))
|
|
|
|
# Delete the record in emby table
|
|
cursor.execute("DELETE FROM emby WHERE emby_id = ?", (id,))
|
|
|
|
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
|
|
|
|
if imageUrl:
|
|
|
|
cacheimage = False
|
|
|
|
cursor.execute("SELECT url FROM art WHERE media_id = ? AND media_type = ? AND type = ?", (kodiId, mediaType, imageType,))
|
|
try:
|
|
url = cursor.fetchone()[0]
|
|
except: # Image does not exists
|
|
cacheimage = True
|
|
self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 1)
|
|
query = "INSERT INTO art(media_id, media_type, type, url) values(?, ?, ?, ?)"
|
|
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
|
|
else:
|
|
if url != imageUrl:
|
|
cacheimage = True
|
|
self.logMsg("Updating Art Link for kodiId: %s (%s) -> (%s)" % (kodiId, url, imageUrl), 1)
|
|
query = "UPDATE art SET url = ? WHERE media_id = ? and media_type = ? and type = ?"
|
|
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
|
|
|
|
# Cache fanart textures in Kodi texture cache
|
|
if cacheimage and "fanart" in imageType:
|
|
self.textureCache.CacheTexture(imageUrl)
|
|
|
|
def AddGenresToMedia(self, id, genres, mediatype, cursor):
|
|
|
|
if genres:
|
|
|
|
for genre in genres:
|
|
|
|
idGenre = None
|
|
cursor.execute("SELECT idGenre as idGenre FROM genre WHERE strGenre = ?", (genre,))
|
|
try:
|
|
idGenre = cursor.fetchone()[0]
|
|
except: # Create the genre
|
|
cursor.execute("select coalesce(max(idGenre),0) as idGenre from genre")
|
|
idGenre = cursor.fetchone()[0] + 1
|
|
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
|
cursor.execute(query, (idGenre, genre))
|
|
finally: # Assign the genre to item
|
|
if "album" in mediatype:
|
|
query = "INSERT OR REPLACE INTO album_genre(idGenre, idAlbum) values(?, ?)"
|
|
cursor.execute(query, (idGenre, id))
|
|
elif "song" in mediatype:
|
|
query = "INSERT OR REPLACE INTO song_genre(idGenre, idSong) values(?, ?)"
|
|
cursor.execute(query, (idGenre, id)) |