# -*- coding: utf-8 -*- # script.module.python.koding.aio # Python Koding AIO (c) by TOTALREVOLUTION LTD (support@trmc.freshdesk.com) # Python Koding AIO is licensed under a # Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. # You should have received a copy of the license along with this # work. If not, see http://creativecommons.org/licenses/by-nc-nd/4.0. # Please make sure you've read and understood the license, this code can NOT be used commercially # and it can NOT be modified and redistributed. If you're found to be in breach of this license # then any affected add-ons will be blacklisted and will not be able to work on the same system # as any other add-ons which use this code. Thank you for your cooperation. import os import sys try: from sqlite3 import dbapi2 as database except: from pysqlite2 import dbapi2 as database import xbmc import xbmcaddon import xbmcgui import xbmcvfs from __init__ import Caller from filetools import Physical_Path # Put this in a try statement, when called from a service it will throw an error otherwise try: try: ADDON_ID = xbmcaddon.Addon().getAddonInfo('id') except: ADDON_ID = Caller() AddonVersion = xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('version') profile_path = xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('profile') addon_db_path = Physical_Path(os.path.join(profile_path,'database.db')) except: pass dbcur, dbcon = None, None dialog = xbmcgui.Dialog() #---------------------------------------------------------------- def _connect_to_db(): """ internal command ~""" def dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d xbmcvfs.mkdirs(profile_path) db_location = os.path.join(profile_path.decode('utf-8'),'database.db') db_location = Physical_Path(db_location) dbcon = database.connect(db_location) dbcon.row_factory = dict_factory dbcur = dbcon.cursor() return (dbcur, dbcon) #---------------------------------------------------------------- def _execute_db_string(sql_string, commit = True): """ internal command ~""" global dbcur, dbcon if dbcur is None or dbcon is None: dbcur, dbcon = _connect_to_db() dbcur.execute(sql_string) if commit: dbcon.commit() results = [] for result in dbcur: results.append(result) return results #---------------------------------------------------------------- # TUTORIAL # def Add_To_Table(table, spec, abort_on_error=False): """ Add a row to the table in /userdata/addon_data//database.db CODE: Add_To_Table(table, spec) AVAILABLE PARAMS: (*) table - The table name you want to query (*) spec - Sent through as a dictionary this is the colums and constraints. abort_on_error - Default is set to False but set to True if you want to abort the process when it hits an error. EXAMPLE CODE: create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}} koding.Create_Table("test_table", create_specs) add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"} add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"} koding.Add_To_Table("test_table", add_specs1) koding.Add_To_Table("test_table", add_specs2) results = koding.Get_All_From_Table("test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB RESULTS', final_results) koding.Remove_Table('test_table') ~""" global dbcon sql_string = "INSERT INTO %s (" % table keys = [] values = [] if type(spec) != list: spec = [spec] for item in spec: for key in item.keys(): keys.append(key) values.append(item[key]) for key in keys: sql_string += "%s, " % key sql_string = sql_string[:-2] sql_string += ") Values (" for value in values: sql_string += "\"%s\", " % value sql_string = sql_string[:-2] sql_string += ")" try: _execute_db_string(sql_string, commit=False) except: if abort_on_error: dbcon.rollback() raise Exception() continue dbcon.commit() #---------------------------------------------------------------- # TUTORIAL # def Add_Multiple_To_Table(table, keys=[], values=[]): """ This will allow you to add multiple rows to a table in one big (fast) bulk command The db file is: /userdata/addon_data//database.db CODE: Add_To_Table(table, spec) AVAILABLE PARAMS: (*) table - The table name you want to query (*) keys - Send through a list of keys you want to add to (*) values - A list of values you want to add, this needs to be a list of lists (see example below) EXAMPLE CODE: create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}} koding.Create_Table("test_table", create_specs) dialog.ok('ADD TO TABLE','Lets add the details of 3 add-ons to "test_table" in our database.') mykeys = ["name","id"] myvalues = [("YouTube","plugin.video.youtube"), ("vimeo","plugin.video.vimeo"), ("test2","plugin.video.test2")] koding.Add_Multiple_To_Table(table="test_table", keys=mykeys, values=myvalues) results = koding.Get_All_From_Table("test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB RESULTS', 'Below are details of the items pulled from our db:\n\n%s'%final_results) koding.Remove_Table('test_table') ~""" dbcur, dbcon = _connect_to_db() sql_string = "INSERT INTO %s (" % table sql_2 = '' if type(keys) != list: keys = [keys] if type(values) != list: values = [values] for item in keys: if not item.startswith('`'): item = r'`'+item if not item.endswith('`'): item = item+r'`' xbmc.log('ITEM: %s'%item,2) sql_string += "%s, " % item sql_2 += "?," sql_string = "%s) VALUES (%s)"%(sql_string[:-2], sql_2[:-1]) dbcur.executemany(sql_string, values) dbcon.commit() #---------------------------------------------------------------- # TUTORIAL # def Create_Table(table, spec): """ Create a new table in the database at /userdata/addon_data//database.db CODE: Create_Table(table, spec) AVAILABLE PARAMS: (*) table - The table name you want to query (*) spec - Sent through as a dictionary this is the colums and constraints. EXAMPLE CODE: create_specs = { "columns":{"name":"TEXT", "id":"TEXT"}, "constraints":{"unique":"id"} } koding.Create_Table("test_table", create_specs) dialog.ok('TABLE CREATED','A new table has been created in your database and the id column has been set as UNIQUE.') my_specs = {"name":"YouTube", "id":"plugin.video.youtube"} try: koding.Add_To_Table("test_table", my_specs) koding.Add_To_Table("test_table", my_specs) except: dialog.ok('FAILED TO ADD','Could not add duplicate items because the the column "id" is set to be UNIQUE') results = koding.Get_All_From_Table("test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB RESULTS', final_results) koding.Remove_Table('test_table') ~""" sql_string = "CREATE TABLE IF NOT EXISTS %s (" % table columns = spec.get("columns", {}) constraints = spec.get("constraints", {}) for key in columns.keys(): if not columns[key]: columns[key] = "TEXT" sql_string += "%s %s, " % (key, columns[key]) for key in constraints.keys(): sql_string += "%s(%s), " % (key, constraints[key]) sql_string = sql_string[:-2] sql_string += ");" _execute_db_string(sql_string) #---------------------------------------------------------------- # TUTORIAL # def DB_Query(db_path, query, values=''): """ Open a database and either return an array of results with the SELECT SQL command or perform an action such as INSERT, UPDATE, CREATE. CODE: DB_Query(db_path, query, [values]) AVAILABLE PARAMS: (*) db_path - the full path to the database file you want to access. (*) query - this is the actual db query you want to process, use question marks for values values - a list of values, even if there's only one value it must be sent through as a list item. IMPORTANT: Directly accessing databases which are outside of your add-ons domain is very much frowned upon. If you need to access a built-in kodi database (as shown in example below) you should always use the JSON-RPC commands where possible. EXAMPLE CODE: import filetools dbpath = filetools.DB_Path_Check('addons') db_table = 'addon' kodi_version = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2])) if kodi_version >= 17: db_table = 'addons' db_query = koding.DB_Query(db_path=dbpath, query='SELECT * FROM %s WHERE addonID LIKE ? AND addonID NOT LIKE ?'%db_table, values=['%youtube%','%script.module%']) koding.Text_Box('DB SEARCH RESULTS',str(db_query)) ~""" db_dict = [] db_path = Physical_Path(db_path) con = database.connect(db_path) cur = con.cursor() if query.upper().startswith('SELECT'): if values == '': cur.execute(query) else: cur.execute(query, values) names = list(map(lambda x: x[0], cur.description)) for rows in iter(cur.fetchmany, []): for row in rows: temp_dict = {} for idx, col in enumerate(cur.description): temp_dict[col[0]] = row[idx] db_dict.append(temp_dict) return db_dict elif query.upper().startswith('CREATE'): cur.execute(query) con.commit() # ANY NON SELECT QUERY (UPDATE, INSERT ETC.) else: try: if values == '': cur.executemany(query) con.commit() else: cur.executemany(query, values) con.commit() except: if values == '': cur.execute(query) con.commit() else: cur.execute(query, values) con.commit() cur.close() #---------------------------------------------------------------- # TUTORIAL # def Get_All_From_Table(table): """ Return a list of all entries from a specific table in /userdata/addon_data//database.db CODE: Get_All_From_Table(table) AVAILABLE PARAMS: (*) table - The table name you want to query EXAMPLE CODE: create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}} koding.Create_Table("test_table", create_specs) add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"} add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"} koding.Add_To_Table("test_table", add_specs1) koding.Add_To_Table("test_table", add_specs2) results = koding.Get_All_From_Table("test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB RESULTS', final_results) koding.Remove_Table('test_table') ~""" try: return _execute_db_string("SELECT * FROM %s" % table) except: return [] #---------------------------------------------------------------- # TUTORIAL # def Get_From_Table(table, spec=None, default_compare_operator="="): """ Return a list of all entries matching a specific criteria from the database stored at: /userdata/addon_data//database.db CODE: Get_From_Table(table, spec, compare_operator) AVAILABLE PARAMS: (*) table - The table name you want to query spec - This is the query value, sent through as a dictionary. default_compare_operator - By default this is set to '=' but could be any other SQL query string such as 'LIKE', 'NOT LIKE', '!=' etc. EXAMPLE CODE: create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}} koding.Create_Table("test_table", create_specs) add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"} add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"} koding.Add_To_Table("test_table", add_specs1) koding.Add_To_Table("test_table", add_specs2) results = koding.Get_From_Table(table="test_table", spec={"name":"%vim%"}, default_compare_operator="LIKE") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB CONTENTS', final_results) koding.Remove_Table('test_table') ~""" if spec == None: return Get_All_From_Table() sql_string = "SELECT * FROM %s WHERE " % table for key in spec.keys(): if type(spec[key]) == dict: value = spec[key]["value"] column_compare_operator = spec[key].get("compare_operator", default_compare_operator) else: value = spec[key] column_compare_operator = default_compare_operator sql_string += "%s %s \"%s\" AND " % (key, column_compare_operator, value) sql_string = sql_string[:-5] try: return _execute_db_string(sql_string, commit=False) except: return [] #---------------------------------------------------------------- # TUTORIAL # def Remove_From_Table(table, spec, default_compare_operator="=", abort_on_error=False): """ Remove entries in the db table at /userdata/addon_data//database.db CODE: Remove_From_Table(table, spec, [compare_operator]) AVAILABLE PARAMS: (*) table - The table name you want to query spec - This is the query value, sent through as a dictionary. default_compare_operator - By default this is set to '=' but could be any other SQL query string such as 'LIKE', 'NOT LIKE', '!=' etc. EXAMPLE CODE: create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}} koding.Create_Table(table="test_table", spec=create_specs) add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"} add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"} koding.Add_To_Table(table="test_table", spec=add_specs1) koding.Add_To_Table(table="test_table", spec=add_specs2) results = koding.Get_All_From_Table(table="test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('DB CONTENTS', final_results) dialog.ok('REMOVE ITEM','We will now remove vimeo from the table, lets see if it worked...') koding.Remove_From_Table(table="test_table", spec={"name":"vimeo"}) results = koding.Get_All_From_Table(table="test_table") final_results = '' for item in results: final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"]) koding.Text_Box('NEW DB CONTENTS', final_results) koding.Remove_Table('test_table') ~""" global dbcon sql_string = "DELETE FROM %s WHERE " % table if type(spec) != list: spec = [spec] for item in spec: for key in item.keys(): if type(item[key]) == dict: value = item[key]["value"] column_compare_operator = item[key].get("compare_operator", default_compare_operator) else: value = item[key] column_compare_operator = default_compare_operator sql_string += "%s %s \"%s\" AND " % (key, column_compare_operator, value) sql_string = sql_string[:-4] try: _execute_db_string(sql_string, commit=False) except: if abort_on_error: dbcon.rollback() raise Exception() continue dbcon.commit() #---------------------------------------------------------------- # TUTORIAL # def Remove_Table(table): """ Use with caution, this will completely remove a database table and all of it's contents. The only database you can access with this command is your add-ons own db file called database.db CODE: Remove_Table(table) AVAILABLE PARAMS: (*) table - This is the name of the table you want to permanently delete. EXAMPLE CODE: dialog.ok('REMOVE TABLE','It\'s a bit pointless doing this as you can\'t physically see what\'s happening so you\'ll just have to take our word it works!') koding.Remove_Table('test_table') ~""" sql_string = "DROP TABLE IF EXISTS %s;" % table _execute_db_string(sql_string) #---------------------------------------------------------------- def reset_db(): global dbcon, dbcur dbcur, dbcon = None, None