How to build a game library
#1
Information 
Hi guys,

Few months ago I've started learning Python, by building a Kodi addon. I've built a really simple Python wrapper for moonlight-embedded, and right now I'm able to retrieve the game list from the host, close Kodi, start the streaming of a game and restart Kodi once the streaming is over.

The next step will be retrieve the infos of available games from IGDB, save them in Kodi and build a source list. Here I'm getting lost. I cannot find a guide that explain me how to save infos in Kodi. I've read the Romanvm's guide, but that infos seems to be hardcoded in main.py file. The Database wiki page says that Kodi maintains databases on it's own, and a developer should not write to them. So, where should I save my game infos, and how I'm supposed to retrieve those infos and build a list? For infos I mean game name, artworks, rating, etc. etc.

Thanks in advance!
Reply
#2
Custom data for your addon can be stored in your addons' "profile" directory:

python:
profile_dir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')

You can save data in any format you like: plain text, CSV, Python pickle, SQlite database.

As for extracting ("scraping") information from websites with Python, it's a separate big topic not limited to Kodi, and there is plenty info in the Internet. For classic server-rendered web-pages BeautifulSoup4 with html5lib is still a good choice, but with modern JavaScript-based websites the situation gets more complicated. You need to use either a headless browser engine, which is not a trivial task for a Kodi addon, or to reverse-engineer the front-end JavaScript application to find REST endpoints it gets its data from and use those endpoints in your addon to fetch data.

UPD: fixed the code snippet above
Reply
#3
Oh cool, so I can create my own SQLite database!

python:

profile_folder = ADDON.getAddonInfo('profile')
# TODO: Create db in profile_folder and store whatever I want...

Scraping is not a problem. I'm going to use IGDB APIs to retrieve game infos, they seems to be pretty simple to use.

Thanks Roman!
Reply
#4
(2018-11-05, 14:09)OutlawPlz Wrote: So, where should I save my game infos, and how I'm supposed to retrieve those infos and build a list?

Now I understood why you answer me about scraping info! I say that badly... I was referring to retrieve info once they're saved in the database. But now it's not a problem, because I've my own SQLite database.

I've another question. Create a database sounds like a task that should be executed when the add-on is enabled, but I've not found any docs about functions or events that let me execute some code when an add-on is enabled. :/ Is it possible?
Reply
#5
You don't need to create a database explicitly, at least with the built-in sqlite3 connector.
Reply
#6
Mmh I don't get it... I'm planning to use Peewee as ORM to store and retrieve game's info, host's info and moonlight-embedded configs. So I have to connect to the database and create the tables I need. But doing this every time my add-on is called sounds a bit weird to me. :/ That's why I would like to initialize the database only once when my add-on is enabled.

Am I missing something? Of course it could be a bit too much for a Kodi add-on, but I'm doing all this just to learn python.
Reply
#7
sqlite3 connector (Peewwe uses it "under the hood") creates a dababase file on connect if it does not exist. As for table creation, Peewee supports "safe" creation, meaning that tables are created only when they do not exist. So it's safe to run your database initialization code on addon startup.
Reply
#8
Yeah, and that's exactly what I do. So there's no way to run arbitrary code when an add-on is enabled/disabled... Got it.

Thanks Roman as usual. Wink
Reply
#9
(2018-11-08, 20:04)OutlawPlz Wrote: So there's no way to run arbitrary code when an add-on is enabled/disabled..

Not that I know of. Although, when disabling an addon, monitor.abortRequested() flag should be set.
Reply
#10
FYI retro games can be played in Kodi 18.
Reply
#11
Yeah, I've read that has been developed some game specific APIs in Kodi 18. That makes me think that will be a pain build a list of games in Kodi 17 similar to movies list.
Reply
#12
IMO out-of-process emulation is a dead end, and RetroPlayer is the future because it can run all the emulators in-process. At the lower level RetroPlayer is a far more integrated and consistent experience than trying to coordinate between unfamiliar processes. You can put in the extra effort to support v17 for external emulators, but I would just target v18 and above.
Reply
#13
If you find anything from movies lacking in the game window, we can easily copy stuff from the movies window to the games window.
Reply
#14
Hey Garbear, sorry for late reply. Yeah I think you're right about emulators, but Moonlight is not an emulator. Is an open source implementation of Nvidia Gamestream. It will stream your games from a Windows PC to your devices. If RetroPlayer will work even with Moonlight it will be great! By the way, I'll let you know if I'll find something lacking in the game window!
Reply
#15
(2018-11-05, 14:37)Roman_V_M Wrote:
Code:
You can save data in any format you like: plain text, CSV, Python pickle, SQlite database.
 Hello Roman.

I would very much like to save the data I'm using in my own addon as a SQLite DB. Do you know of any working example of this?

I've been trying to develop my own solution, which I post below, but I'm not sure I'm doing things the right way....

What I missed initially and was making this not work was:
-  call commit on the connection after executing statements;
- the profile dir must exist before creating the db.

Bye and thanks, Luís
python:

import xbmc
import sys
import sqlite3
from sqlite3 import Error
import xbmcaddon
import os
import xbmcvfs
import random
       
# code from http://www.sqlitetutorial.net/sqlite-pyt...te-tables/
def setupDB():
    database = os.path.join(profile_dir,"test.db")
        
    xbmc.log("DB file:"+database)
 
    sql_create_votes_table = """ CREATE TABLE IF NOT EXISTS TEST (
                                        user text NOT NULL,
                                        value integer NOT NULL
                                    ); """
 
   
                              
 
    # create a database connection
    conn = create_connection(database)
    if conn is not None:
        # create projects table
        create_table(conn, sql_create_votes_table)
        conn.commit()
    else:
        xbmc.log("Error: cannot create the database connection.")
        
        
    # to be user outside this function
    return conn



      
# Database code
def create_connection(db_file):
    """ create a database connection to the SQLite database
        specified by db_file
    :param db_file: database file
    :return: Connection object or None
    """
    try:
     conn = sqlite3.connect(db_file)
     return conn
    except Error as e:
        xbmc.log("Error: cannot create connection"+' '.join(e))
        print(e)
 
    return None

def create_table(conn, create_table_sql):
    """ create a table from the create_table_sql statement
    :param conn: Connection object
    :param create_table_sql: a CREATE TABLE statement
    :return:
    """
    try:
        c = conn.cursor()
        c.execute(create_table_sql)
    except Error as e:
        xbmc.log("Error: cannot create table:"+' '.join(e))
        print(e)

def insertValue(conn,user,value):
    """ insert new vote on the corresponding table
    :param songid: song identifier
    :param user: user identifier
    :return: void
    """
    if conn is not None:
        try:    
            sqlText="""INSERT INTO TEST (user,value) VALUES (?,?)"""
            xbmc.log("Trying to insert:"+sqlText)
            c = conn.cursor()
            c.execute(sqlText,(user,value))
            conn.commit()
            xbmc.log("Insertion finished")
        except Error as e:
            xbmc.log("Error: cannot insert into:"+' '.join(e))
            print(e)
    else:
        xbmc.log("Error: cannot insert into: no connection available")



# Launch point
if __name__ == '__main__':
   # Get profile dir
   xbmc.log("Starting addon...")

    # Check and possibly create profile dir
   profile_dir = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('profile')).decode('utf-8')
   if xbmcvfs.exists(profile_dir):
       xbmc.log("Prodile dir:"+profile_dir)
   else:
       xbmcvfs.mkdir(profile_dir)
       xbmc.log("Created prodile dir:"+profile_dir)
  
  
   conn=setupDB()
   insertValue(conn,"testUser",random.randint(0, 10000))
      
Reply

Logout Mark Read Team Forum Stats Members Help
How to build a game library0