From b55b4d0b96d91d4c4796da3fab6830e0a73c8868 Mon Sep 17 00:00:00 2001
From: shaun <shaun@bluebit.com.au>
Date: Sat, 5 Nov 2016 14:18:39 +1100
Subject: [PATCH] remove old kodiSQL function use DatabaseCon for DB file paths
 switch last few DB Cons to use the new DatabaseCon context class

---
 resources/lib/artwork.py         | 128 +++++++++++++++----------------
 resources/lib/context_entry.py   |  43 +++++------
 resources/lib/entrypoint.py      |   6 +-
 resources/lib/itemtypes.py       |  70 ++++++++---------
 resources/lib/kodimonitor.py     |   2 +-
 resources/lib/librarysync.py     |   4 +-
 resources/lib/playlist.py        |   2 +-
 resources/lib/read_embyserver.py |   2 +-
 resources/lib/utils.py           |  49 ------------
 9 files changed, 123 insertions(+), 183 deletions(-)

diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py
index 2b828a2d..441dc2de 100644
--- a/resources/lib/artwork.py
+++ b/resources/lib/artwork.py
@@ -13,7 +13,9 @@ import xbmcvfs
 import requests
 
 import image_cache_thread
-from utils import window, settings, dialog, language as lang, kodiSQL, JSONRPC
+from utils import window, settings, dialog, language as lang, JSONRPC
+from database import DatabaseConn
+from contextlib import closing
 
 ##################################################################################################
 
@@ -164,47 +166,49 @@ class Artwork(object):
 
     def _cache_all_video_entries(self, pdialog):
 
-        conn = kodiSQL('video')
-        cursor = conn.cursor()
-        cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
-        result = cursor.fetchall()
-        total = len(result)
-        log.info("Image cache sync about to process %s images", total)
-        cursor.close()
+        with DatabaseConn('video') as conn:
+            with closing(conn.cursor()) as cursor_video:    
+            
+                cursor_video.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
+                result = cursor_video.fetchall()
+                total = len(result)
+                log.info("Image cache sync about to process %s images", total)
+                cursor_video.close()
 
-        count = 0
-        for url in result:
+                count = 0
+                for url in result:
 
-            if pdialog.iscanceled():
-                break
+                    if pdialog.iscanceled():
+                        break
 
-            percentage = int((float(count) / float(total))*100)
-            message = "%s of %s (%s)" % (count, total, len(self.image_cache_threads))
-            pdialog.update(percentage, "%s %s" % (lang(33045), message))
-            self.cache_texture(url[0])
-            count += 1
+                    percentage = int((float(count) / float(total))*100)
+                    message = "%s of %s (%s)" % (count, total, len(self.image_cache_threads))
+                    pdialog.update(percentage, "%s %s" % (lang(33045), message))
+                    self.cache_texture(url[0])
+                    count += 1
 
     def _cache_all_music_entries(self, pdialog):
 
-        conn = kodiSQL('music')
-        cursor = conn.cursor()
-        cursor.execute("SELECT url FROM art")
-        result = cursor.fetchall()
-        total = len(result)
-        log.info("Image cache sync about to process %s images", total)
-        cursor.close()
+        with DatabaseConn('music') as conn:
+            with closing(conn.cursor()) as cursor_music:        
+        
+                cursor_music.execute("SELECT url FROM art")
+                result = cursor_music.fetchall()
+                total = len(result)
+                
+                log.info("Image cache sync about to process %s images", total)
 
-        count = 0
-        for url in result:
+                count = 0
+                for url in result:
 
-            if pdialog.iscanceled():
-                break
+                    if pdialog.iscanceled():
+                        break
 
-            percentage = int((float(count) / float(total))*100)
-            message = "%s of %s" % (count, total)
-            pdialog.update(percentage, "%s %s" % (lang(33045), message))
-            self.cache_texture(url[0])
-            count += 1
+                    percentage = int((float(count) / float(total))*100)
+                    message = "%s of %s" % (count, total)
+                    pdialog.update(percentage, "%s %s" % (lang(33045), message))
+                    self.cache_texture(url[0])
+                    count += 1
 
     @classmethod
     def delete_cache(cls):
@@ -226,16 +230,14 @@ class Artwork(object):
                     log.debug("deleted: %s", filename)
 
         # remove all existing data from texture DB
-        conn = kodiSQL('texture')
-        cursor = conn.cursor()
-        cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
-        rows = cursor.fetchall()
-        for row in rows:
-            table_name = row[0]
-            if table_name != "version":
-                cursor.execute("DELETE FROM " + table_name)
-        conn.commit()
-        cursor.close()
+        with DatabaseConn('texture') as conn:
+            with closing(conn.cursor()) as cursor_texture:  
+                cursor_texture.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
+                rows = cursor_texture.fetchall()
+                for row in rows:
+                    table_name = row[0]
+                    if table_name != "version":
+                        cursor_texture.execute("DELETE FROM " + table_name)
 
     def _add_worker_image_thread(self, url):
 
@@ -428,33 +430,29 @@ class Artwork(object):
 
     @classmethod
     def delete_cached_artwork(cls, url):
-        # Only necessary to remove and apply a new backdrop or poster
-        conn = kodiSQL('texture')
-        cursor = conn.cursor()
+        # Only necessary to remove and apply a new backdrop or poster       
+        with DatabaseConn('texture') as conn:
+            with closing(conn.cursor()) as cursor_texture:  
+                try:
+                    cursor_texture.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
+                    cached_url = cursor_texture.fetchone()[0]
 
-        try:
-            cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
-            cached_url = cursor.fetchone()[0]
+                except TypeError:
+                    log.info("Could not find cached url")
 
-        except TypeError:
-            log.info("Could not find cached url")
+                except OperationalError:
+                    log.info("Database is locked. Skip deletion process.")
 
-        except OperationalError:
-            log.info("Database is locked. Skip deletion process.")
+                else: # Delete thumbnail as well as the entry
+                    thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached_url).decode('utf-8')
+                    log.info("Deleting cached thumbnail: %s", thumbnails)
+                    xbmcvfs.delete(thumbnails)
 
-        else: # Delete thumbnail as well as the entry
-            thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached_url).decode('utf-8')
-            log.info("Deleting cached thumbnail: %s", thumbnails)
-            xbmcvfs.delete(thumbnails)
+                    try:
+                        cursor_texture.execute("DELETE FROM texture WHERE url = ?", (url,))
+                    except OperationalError:
+                        log.debug("Issue deleting url from cache. Skipping.")
 
-            try:
-                cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
-                conn.commit()
-            except OperationalError:
-                log.debug("Issue deleting url from cache. Skipping.")
-
-        finally:
-            cursor.close()
 
     def get_people_artwork(self, people):
         # append imageurl if existing
diff --git a/resources/lib/context_entry.py b/resources/lib/context_entry.py
index d7a7bf0c..fa9a2d05 100644
--- a/resources/lib/context_entry.py
+++ b/resources/lib/context_entry.py
@@ -11,7 +11,7 @@ import api
 import read_embyserver as embyserver
 import embydb_functions as embydb
 import musicutils as musicutils
-from utils import settings, dialog, language as lang, kodiSQL
+from utils import settings, dialog, language as lang
 from dialogs import context
 from database import DatabaseConn
 from contextlib import closing
@@ -165,31 +165,28 @@ class ContextMenu(object):
 
     def _rate_song(self):
 
-        conn = kodiSQL('music')
-        cursor = conn.cursor()
-        query = "SELECT rating FROM song WHERE idSong = ?"
-        cursor.execute(query, (self.kodi_id,))
-        try:
-            value = cursor.fetchone()[0]
-            current_value = int(round(float(value), 0))
-        except TypeError:
-            pass
-        else:
-            new_value = dialog("numeric", 0, lang(30411), str(current_value))
-            if new_value > -1:
+        with DatabaseConn('music') as conn:
+            with closing(conn.cursor()) as cursor_music:            
+                query = "SELECT rating FROM song WHERE idSong = ?"
+                cursor_music.execute(query, (self.kodi_id,))
+                try:
+                    value = cursor_music.fetchone()[0]
+                    current_value = int(round(float(value), 0))
+                except TypeError:
+                    pass
+                else:
+                    new_value = dialog("numeric", 0, lang(30411), str(current_value))
+                    if new_value > -1:
 
-                new_value = int(new_value)
-                if new_value > 5:
-                    new_value = 5
+                        new_value = int(new_value)
+                        if new_value > 5:
+                            new_value = 5
 
-                if settings('enableUpdateSongRating') == "true":
-                    musicutils.updateRatingToFile(new_value, self.api.get_file_path())
+                        if settings('enableUpdateSongRating') == "true":
+                            musicutils.updateRatingToFile(new_value, self.api.get_file_path())
 
-                query = "UPDATE song SET rating = ? WHERE idSong = ?"
-                cursor.execute(query, (new_value, self.kodi_id,))
-                conn.commit()
-        finally:
-            cursor.close()
+                        query = "UPDATE song SET rating = ? WHERE idSong = ?"
+                        cursor_music.execute(query, (new_value, self.kodi_id,))
 
     def _delete_item(self):
 
diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py
index 7c5d6178..a94d46e0 100644
--- a/resources/lib/entrypoint.py
+++ b/resources/lib/entrypoint.py
@@ -180,12 +180,10 @@ def emby_backup():
     shutil.copy(src=xbmc.translatePath("special://database/emby.db").decode('utf-8'),
                 dst=database)
     # Videos database
-    shutil.copy(src=utils.getKodiVideoDBPath(),
-                dst=database)
+    shutil.copy(src=DatabaseConn()._SQL('video'), dst=database)
     # Music database
     if settings('enableMusic') == "true":
-        shutil.copy(src=utils.getKodiMusicDBPath(),
-                    dst=database)
+        shutil.copy(src=DatabaseConn()._SQL('music'), dst=database)
 
     dialog(type_="ok",
            heading="{emby}",
diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py
index 6dda8b38..d4645afc 100644
--- a/resources/lib/itemtypes.py
+++ b/resources/lib/itemtypes.py
@@ -6,7 +6,9 @@ import logging
 
 import read_embyserver as embyserver
 from objects import Movies, MusicVideos, TVShows, Music
-from utils import settings, kodiSQL
+from utils import settings
+from database import DatabaseConn
+from contextlib import closing
 
 #################################################################################################
 
@@ -58,46 +60,40 @@ class Items(object):
         if pdialog:
             pdialog.update(heading="Processing %s: %s items" % (process, total))
 
-        for itemtype in items:
+        # this is going to open a music connection even if it is not needed but
+        # I feel that is better than trying to sort out the login yourself
+        with DatabaseConn('music') as conn:
+            with closing(conn.cursor()) as cursor_music:
+            
+                for itemtype in items:
 
-            # Safety check
-            if not itemtypes.get(itemtype):
-                # We don't process this type of item
-                continue
+                    # Safety check
+                    if not itemtypes.get(itemtype):
+                        # We don't process this type of item
+                        continue
 
-            itemlist = items[itemtype]
-            if not itemlist:
-                # The list to process is empty
-                continue
+                    itemlist = items[itemtype]
+                    if not itemlist:
+                        # The list to process is empty
+                        continue
 
-            musicconn = None
+                    if itemtype in ('MusicAlbum', 'MusicArtist', 'AlbumArtist', 'Audio'):
+                        if self.music_enabled:
+                            items_process = itemtypes[itemtype](embycursor, cursor_music, pdialog) # see note above
+                        else:
+                            # Music is not enabled, do not proceed with itemtype
+                            continue
+                    else:
+                        update_videolibrary = True
+                        items_process = itemtypes[itemtype](embycursor, kodicursor, pdialog)
 
-            if itemtype in ('MusicAlbum', 'MusicArtist', 'AlbumArtist', 'Audio'):
-                if self.music_enabled:
-                    musicconn = kodiSQL('music')
-                    musiccursor = musicconn.cursor()
-                    items_process = itemtypes[itemtype](embycursor, musiccursor, pdialog)
-                else:
-                    # Music is not enabled, do not proceed with itemtype
-                    continue
-            else:
-                update_videolibrary = True
-                items_process = itemtypes[itemtype](embycursor, kodicursor, pdialog)
+                    if process == "added":
+                        items_process.add_all(itemtype, itemlist)
+                    elif process == "remove":
+                        items_process.remove_all(itemtype, itemlist)
+                    else:
+                        process_items = self.emby.getFullItems(itemlist)
+                        items_process.process_all(itemtype, process, process_items, total)
 
 
-            if process == "added":
-                items_process.add_all(itemtype, itemlist)
-            elif process == "remove":
-                items_process.remove_all(itemtype, itemlist)
-            else:
-                process_items = self.emby.getFullItems(itemlist)
-                items_process.process_all(itemtype, process, process_items, total)
-
-
-            if musicconn is not None:
-                # close connection for special types
-                log.info("updating music database")
-                musicconn.commit()
-                musiccursor.close()
-
         return (True, update_videolibrary)
diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py
index 2454c4ef..c0a9bd72 100644
--- a/resources/lib/kodimonitor.py
+++ b/resources/lib/kodimonitor.py
@@ -11,7 +11,7 @@ import xbmcgui
 import downloadutils
 import embydb_functions as embydb
 import playbackutils as pbutils
-from utils import window, settings, kodiSQL
+from utils import window, settings
 from ga_client import log_error
 from database import DatabaseConn
 from contextlib import closing
diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py
index 31622fb4..94b52997 100644
--- a/resources/lib/librarysync.py
+++ b/resources/lib/librarysync.py
@@ -291,7 +291,7 @@ class LibrarySync(threading.Thread):
                         continue
 
                     startTime = datetime.now()
-                    completed = process[itemtype](cursor_emby, kodicursor, pDialog)
+                    completed = process[itemtype](cursor_emby, cursor_video, pDialog)
                     if not completed:
                         xbmc.executebuiltin('InhibitIdleShutdown(false)')
                         utils.setScreensaver(value=screensaver)
@@ -913,7 +913,7 @@ class LibrarySync(threading.Thread):
 
             if not startupComplete:
                 # Verify the video database can be found
-                videoDb = utils.getKodiVideoDBPath()
+                videoDb = DatabaseConn()._SQL('video')
                 if not xbmcvfs.exists(videoDb):
                     # Database does not exists
                     log.error(
diff --git a/resources/lib/playlist.py b/resources/lib/playlist.py
index 1eaac424..82137c0f 100644
--- a/resources/lib/playlist.py
+++ b/resources/lib/playlist.py
@@ -11,7 +11,7 @@ import playutils
 import playbackutils
 import embydb_functions as embydb
 import read_embyserver as embyserver
-from utils import window, kodiSQL, JSONRPC
+from utils import window, JSONRPC
 from database import DatabaseConn
 from contextlib import closing
 
diff --git a/resources/lib/read_embyserver.py b/resources/lib/read_embyserver.py
index 5ea9cafa..e2cec77f 100644
--- a/resources/lib/read_embyserver.py
+++ b/resources/lib/read_embyserver.py
@@ -8,7 +8,7 @@ import hashlib
 import xbmc
 
 import downloadutils
-from utils import window, settings, kodiSQL
+from utils import window, settings
 from database import DatabaseConn
 from contextlib import closing
 
diff --git a/resources/lib/utils.py b/resources/lib/utils.py
index b82a167f..9191c702 100644
--- a/resources/lib/utils.py
+++ b/resources/lib/utils.py
@@ -120,55 +120,6 @@ def should_stop():
     else: # Keep going
         return False
 
-def kodiSQL(media_type="video"):
-
-    if media_type == "emby":
-        dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
-    elif media_type == "texture":
-        dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
-    elif media_type == "music":
-        dbPath = getKodiMusicDBPath()
-    else:
-        dbPath = getKodiVideoDBPath()
-
-    if settings('dblock') == "true":
-        connection = sqlite3.connect(dbPath, isolation_level=None, timeout=20)
-    else:
-        connection = sqlite3.connect(dbPath, timeout=20)
-    return connection
-
-def getKodiVideoDBPath():
-
-    dbVersion = {
-
-        "13": 78,   # Gotham
-        "14": 90,   # Helix
-        "15": 93,   # Isengard
-        "16": 99,   # Jarvis
-        "17": 107   # Krypton
-    }
-
-    dbPath = xbmc.translatePath(
-                "special://database/MyVideos%s.db"
-                % dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
-    return dbPath
-
-def getKodiMusicDBPath():
-
-    dbVersion = {
-
-        "13": 46,   # Gotham
-        "14": 48,   # Helix
-        "15": 52,   # Isengard
-        "16": 56,   # Jarvis
-        "17": 60    # Krypton
-    }
-
-    dbPath = xbmc.translatePath(
-                "special://database/MyMusic%s.db"
-                % dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
-    return dbPath
-
 #################################################################################################
 # Utility methods