Archive:Write Python Scripts

From Official Kodi Wiki
Jump to navigation Jump to search

XBMC features a Python Scripts Engine and WindowXML application framework (a XML-based widget toolkit for creating GUI window structure) in a similar fashion to Apple Mac OS X Dashboard Widgets and Microsoft Gadgets in Windows Sidebar. So normal users can add new functionality to XBMC themselves (using the easy to learn Python programming language) without an illegal copy of the XDK and without knowledge of the complex C/C++ programming language. Current plugin scripts include functions like Internet-TV and movie-trailer browsers, weather forecast and cinemaguides, TV-guides (EPG), e-mail clients, instant messaging, train-timetables, scripts to front-end control PVR software and hardware (like: MediaPortal, MythTV, TiVo, ReplayTV, Dreambox/DBox2), Internet-radio-station browsers (example SHOUTcast, Xm radio, Sirius Satellite Radio), P2P file-sharing downloaders (BitTorrent), IRC, also casual games (sometimes also referred to as mini-games or party-games) such as Tetris, Snake, Space Invaders, Sudoku, and much more.

Please feel free to add more samples of simple scripting functions with comments that you know or figure out while you're learning to script. Adding anything at all no matter how basic, if its not already here add it! someone will more then likely benefit from it. The more difficult your snippet please heavily comment it with "#" don't be stingy on the comments you can never have too much information, what's simple to you may make no sense at all to someone else, also URLs that were helpful to what you were doing would be great to add to your snippet or to the bookmark section (python sites, chatrooms, etc):

Python Example

Description: Python won't run the lines that we put a '#' in front of they are considered as “commented out” python will skip them. <python>

  1. import the XBMC libraries so we can use the controls and functions of XBMC

import xbmc, xbmcgui

  1. name and create our window

class BlahMainWindow(xbmcgui.Window):

   # and define it as self
   def __init__(self):
   # add picture control to our window (self) with a hardcoded path name to picture
   self.addControl(xbmcgui.ControlImage(0,0,720,480, 'Q:\\scripts\\background.jpg'))
  1. store our window as a short variable for easy of use

W = BlahMainWindow()

  1. run our window we created with our background jpeg image

W.doModal()

  1. after the window is closed, Destroy it.

del W </python>

Python resources

Python resources, guides and documentation directly related to XBMC

XBMC Online Manual Python related sections
Other XBMC Python related articles

Other python resources (indirectly related to XBMC scripting)

IRC Channels

 /server irc.freenode.net /join #xbmc-scripting
 /server irc.freenode.net /join #python
 /server irc.freenode.net /join #python-cleese
 /server irc.freenode.net /join #python-gilliam

Basic Information

To code python all you basically need is notepad or any other form of texteditor its not very hard to figure out the basics with the documents in Bookmark section above, have a run through them first if you have no clue whats going on in the code snippets to enlighten yourself a bit. – I would suggest looking at the ALEX's Tutorial below first to start, and remember python is all about indents...

Structure

  • Please keep all your files related to your script in its own folder.
  • Name the Main File default.py
  • If you want a thumbnail call it default.tbn (tbn can be jpg or png)

Use this code to find the path you want <python> Root = os.getcwd().replace(";","")+"\\" </python> The Above line will return a folder so its like q:\\scripts\\GoogleScript\\ or where ever the script is located

As of the 13th of July, MyScripts now flatterns meaning it will it will no longer shows the folders and their contents if there is a default.py in the folder (won't let you view any deeper)

Another nice way to have all your files inside your app is to put them inside a lib/ dir inside it, so "the first level" can only have the default.py and default.tbn files. To have all your sub-scripts in that way you have to put at the head of your script something similar to this code: <python> LIB_DIR = ROOT+"libary\\" sys.path.append(LIB_DIR)

import your_own_personal_python_library </python> so you'll be able to import your files as if them were on the root directory of your python app.

Basic Steps

  1. open a text document.
  2. paste some code in there. READ: ALEX's Tutorial
  3. save it to any name you wish with .py on the end for the file extension. (myscript.py)
  4. ftp it to the scripts folder in XBMC (F:/xbmc/scripts/)
  5. in xbmc goto the submenu naturally beside the power button on the skin in Project Mayhem 1.
  6. click scripts in the submenu.
  7. find your script you uploaded and run it.
  8. “White Button” on the controller for python debug whilst in scripts window (Added:13-02-05)

To find out a lot of stuff of what there is available to control in XBMC take a look at these documents from the CVS: XBMC python documentation

Code Snippets

(Warning: Some code may require you to first create a window or something along those lines but might not be specified)

Thanks to the following contributors:

  • ThreeZee (on EFNet: ThreeZee)
  • EnderW
  • alexsolex
  • Donno

XBMC Core Player Options

Play A File

If you want to play a file this is how, you dont need to make it a variable but it makes your code a little more cleaner. (this code doesnt need a window but import your xbmc libraries as normal) <python>

  1. variable to contain the file location

file = 'q:\\scripts\\Music\\Underworld-BornSlippy.mp3'

  1. tell xbmc to play our file we specified in the above variable

xbmc.Player().play(file) </python>

Fetching artist name and song title from currently playing song

By using the code example below you can get the song title and artist name of the song currently playing in XBMC. <python>

  1. Import XBMC module

import xbmc

  1. First we need to create an object containing the getMusicInfoTag() class from XBMC.Player().
  2. This is in order to use the same instance of the class twice and not create a new class
  3. for every time we query for information.
  4. This is a bit important to notice, as creating many instances is rarely
  5. a good thing to do (unless you need it, but not in this case).

tag = xbmc.Player().getMusicInfoTag()

  1. Now tag contains the getMusicInfoTag() class which then again contains song information.
  2. Now we use this object to get the data by calling functions within that class:

artist = tag.getArtist() title = tag.getTitle()

  1. Now you have two strings containing the information. An example of what you could do next is to print it:

print "Playing: " + artist " - " + title

  1. This will produce i.e: "Playing: AC/DC - Back in black"

</python>

XBMC GUI

Also see WindowXML GUI Toolkit/WindowXML

Adding Buttons the Nice Way

Using The following addButton and setupButtons <python> import xbmcgui

try: Emulating = xbmcgui.Emulating except: Emulating = False

class Example(xbmcgui.Window):

   """
       Example Showing Of Using Sub Buttons Module to Create
       Buttons on a Window
   """
   def __init__(self,):
       if Emulating: xbmcgui.Window.__init__(self)
       setupButtons(self,10,10,100,30,"Vert")
       self.h1 = addButon(self,"Click Me")
       self.something = addButon(self,"Something")
       self.btn_quit = addButon(self,"Quit")
   def onControl(self, c):
       if self.h1 == c:
           print "hey"
       if self.something == c:
           print "you press med"
       if self.btn_quit == c:
           self.close()
      1. The adding button Code (only really need this bit)

def setupButtons(self,x,y,w,h,a="Vert",f=None,nf=None):

   self.numbut  = 0
   self.butx = x
   self.buty = y
   self.butwidth = w
   self.butheight = h
   self.butalign = a
   self.butfocus_img = f
   self.butnofocus_img = nf

def addButon(self,text):

   if self.butalign == "Hori":
       c =  xbmcgui.ControlButton(self.butx + (self.numbut * self.butwidth),self.buty,self.butwidth,self.butheight,text,self.butfocus_img,self.butnofocus_img)
       self.addControl(c)
   elif self.butalign == "Vert":
       c = xbmcgui.ControlButton(self.butx ,self.buty + (self.numbut * self.butheight),self.butwidth,self.butheight,text,self.butfocus_img,self.butnofocus_img)
       self.addControl(c)
   self.numbut += 1
   return c
      1. The End of adding button Code

Z = Example() Z.doModal() del Z </python>

Scaling your script using setCoordinateResolution()

Example: Skinned for PAL resolution <python>

  1. Import the XBMC/XBMCGUI modules.

import xbmc, xbmcgui

  1. resolution values
  2. 1080i = 0
  3. 720p = 1
  4. 480p = 2
  5. 480p16x9 = 3
  6. ntsc = 4
  7. ntsc16x9 = 5

pal = 6

  1. pal16x9 = 7
  2. pal60 = 8
  3. pal6016x9 = 9

class MyScript(xbmcgui.Window):

   def __init__(self):
       self.setResolution(pal)
   def setResolution(self, skinnedResolution):
       # get current resolution
       currentResolution = self.getResolution()
       offset = 0
       # if current and skinned resolutions differ and skinned resolution is not
       # 1080i or 720p (they have no 4:3) calculate widescreen offset
       if ((not (currentResolution == skinnedResolution)) and skinnedResolution > 1):
           # check if current resolution is 16x9
           if (currentResolution == 0 or currentResolution % 2): iCur16x9 = 1
           else: iCur16x9 = 0
           # check if skinned resolution is 16x9
           if (skinnedResolution % 2): i16x9 = 1
           else: i16x9 = 0
           # calculate offset
           offset = iCur16x9 - i16x9
       self.setCoordinateResolution(skinnedResolution + offset)
  1. We need to link the class to an object, and doModal to display it.

My_Window = MyScript() My_Window.doModal() del My_Window </python>

Scaling your script for any size screen

Based on NTSC 720x480 <python>

  1. Import the XBMC/XBMCGUI modules.

import xbmc, xbmcgui

class MyScript(xbmcgui.Window):

   def __init__(self):
       if Emulating: xbmcgui.Window.__init__(self)    
       # This will calculate the actual screen size to 720x480 ratio
       self.scaleX = ( float(self.getWidth())  / float(720) )
       self.scaleY = ( float(self.getHeight()) / float(480) )
       
       self.addControl(xbmcgui.ControlImage(0, 0, int(720 * self.scaleX), int(480 * self.scaleY), "Q:\\scripts\\background.gif"))
       self.addControl(xbmcgui.ControlImage(int(0 * self.scaleX), int(23 * self.scaleY), int(720 * self.scaleX), int(53 * self.scaleY), "Q:\\scripts\\top.gif"))
       # Any X (width / left) setting should be changed to * self.scaleX
       # i.e. The above was: self.addControl(xbmcgui.ControlImage(0, 23, 720, 53, "Q:\\scripts\\top.gif"))
       # This change will make the controls/images/etc scale to the screen.
  1. We need to link the class to an object, and doModal to display it.

My_Window = MyScript() My_Window.doModal() del My_Window </python>

String Manipulation

Add strings of text together

<python>

  1. add strings of text together use the "+"

'Cookie' + ' Monster'

  1. If was added to a label etc would look like: Cookie Monster

</python>

Split strings of text apart into variables

<python>

  1. Our variable of a string we want to split up.

data = '1|(123)456-7890|JimmyRay|06262305'

  1. make sure our data is a string and save it in another variable

varData = str(data)

  1. Split our string at the character '|' or whatever one you specify
  2. could be a space if you wish and save them in the specified variables

i, Name, Number, DT = varData.split('|') </python>

Replace strings of text with other text

<python>

  1. letter or text we want to replace current text with

rplText = 'cat'

  1. current text we want to replace

fndText = 'dog'

  1. String we want to replace those words in

strText = 'dogbatdog'

  1. our new string variable which would be 'catbatcat' now
  2. or basically strText.replace('dog', 'cat')

strOutput = strText.replace(fndText, rplText) print strOutput </python>

Convert Strings, Integer

<python>

  1. Number variable notice no quotes

NUM = 43

  1. String variable notice quotes

SNUM = '43'

  1. Convert our variable to STRING

S1 = str(NUM)

  1. Convert our variable to INTEGER

S2 = int(SNUM) print S1 + S2 </python>

Check if text is in a string

Note this is case sensitive, so you may want to use mystring.lower() and have the “world” all in lower case. <python> """ The output of this script is

 world is in the string
 data is not in the string

""" mystring = "Hello world" if "world" in mystring:

   print "world is in the string"

else:

   print "world is not in the string"
   

if "data" in mystring:

   print "data is in the string"

else:

   print "data is not in the string"

</python>

Imports

Import Time

Delay

<python>

  1. import our time class

import time

  1. call time with the sleep function for ten seconds or however many wanted
  2. until script continues on running.

time.sleep(10) </python>

System Clock

<python>

  1. Import our time class

import time

  1. call time with strftime function followed by the formatting we want to use
  2. you can put any character between the format symbols you want EX: '/'

Time = str(time.strftime ('%H:%M:%S%p')) Date = str(time.strftime ('%d/%a/%Y')) print Date + Time

  1. END OF CODE
  1. FORMAT DIRECTIVES

Directive | Meaning Notes - - - - - - - - - - - - - - - %a Locales abbreviated weekday name. %A Locales full weekday name. %b Locales abbreviated month name. %B Locales full month name. %c Locales appropriate date and time representation. %d Day of the month as a decimal number [01,31]. %H Hour (24-hour clock) as a decimal number [00,23]. %I Hour (12-hour clock) as a decimal number [01,12]. %j Day of the year as a decimal number [001,366]. %m Month as a decimal number [01,12]. %M Minute as a decimal number [00,59]. %p Locales equivalent of either AM or PM. (1) %S Second as a decimal number [00,61]. (2) %U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3) %w Weekday as a decimal number [0(Sunday),6]. %W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3) %x Locale's appropriate date representation. %X Locale's appropriate time representation. %y Year without century as a decimal number [00,99]. %Y Year with century as a decimal number. %Z Time zone name (no characters if no time zone exists). </python>

Import OS

Retrieving the contents of a directory

<python>

  1. need to use os functions

import os

  1. Get the directory contents and put them in **lstDirList**

lstDirList = os.listdir("Q:\\scripts\\")

  1. Cycle through each one, performing the functions within indentation
  2. Example is print, you could do anything with it though.
  3. Keep in mind, this will return files AND directories.

for strFileDir in lstDirList:

    print strFileDir

</python>

File Exist

Check to see if file exists on harddrive <python> import os

  1. Pretty self explanatory but basically we check to see if the Me.png file exist
  2. where we specifiy if not the statement returns false so then we tell our variable
  3. to use a different source ex.) NoPhoto.png.

if os.path.isfile('Q:\\scripts\Photos\Me.png') == False: Photo = 'Q:\\scripts\Photos\NoPhoto.png' </python>

Write to a text file

<python>

  1. LF here stands for LOG FILE you can put anything there doesnt really
  2. matter its basically our variable for the location of our text file.

LF = open('Q:\\scripts\\BLAH.LOG', 'a')

  1. Write to our text file the information we have provided and then goto next line in our file.

(so if it was in a loop it would write to the next line instead of everything on the same line) LF.write('Some log information blah blah blah' + '\n')

  1. Close our file so no further writing is posible.

LF.close() </python>

Read text file

<python>

  1. LF here stands for LOG FILE you can put anything there doesnt really
  2. matter it's basically our variable for the location of our text file.

LF = 'Q:\\scripts\\BLAH.LOG'

  1. Opens our Log File to 'r' READ from it.

log = open(LF, 'r')

  1. load our text file into an array for easy access.

for line in log.readlines():

 # Output all of the array text file to the screen.
 print(line[0:-1])
 # Close our text file so no further reading is posible.
 log.close()

</python>

Clear text file

<python>

  1. LF here stands for LOG FILE you can put anything there doesnt really
  2. matter its basically our variable for the location of our text file.

LF = 'Q:\\scripts\\BLAH.LOG'

  1. basically all this is doing is opening our file and write nothing to it so it will be blank.

clearfile = open(FL, 'w')

  1. Close our file so no further writing is possible.

clearfile.close() </python>

Reading a file

<python> listname = [] listoptions = [] f = open('Q:\\test.txt',"r") s = f.write() f.close() ls = s.split("\n") # This means each new line will be loaded into it's own array. for l in ls:

 if l != "":
   item = l.split("=") 
   # The above line will split Something=Hello into item[0] (Something) and item[1] (Hello)
   listname.append(item[0])
   listoptions.append(item[1])

</python>

HTTP

Read URLS from a web page

The following code will search a page for all <a href="urls">Description</a> and store all the urls in one list array and all the descriptions in another <python> import urllib,urllib2 , re from string import split, replace, find

  1. The url in which to use

Base_URL = "http://ftp.iinet.net.au/pub/games/movies/Red_Vs_Blue/Season1/"

  1. Pre-define global Lists

LinkDescription = [] LinkURL = []

WebSock = urllib.urlopen(Base_URL) # Opens a 'Socket' to URL WebHTML = WebSock.read() # Reads Contents of URL and saves to Variable WebSock.close() # Closes connection to url

Temp_Web_URL = re.compile('<a href=["](.*)[.]zip["]>', re.IGNORECASE).findall(WebHTML) # Using find all mentions of stuff using regexp to use wildcards Temp_Web_Desc = re.compile('<a href=["].*[.]zip["]>(.*)</a>').findall(WebHTML) # find it

for urls, desc in zip(Temp_Web_URL,Temp_Web_Desc):

   LinkURL.append(urls[9:-2])    # Adds urls to a list   # note need to add extention for these links to really work
   LinkDescription.append(desc)    # Adds the descrptions as a array)

</python>

Download Files with Progressbar

Download a url from the net and saves to a file and shows the progressbar. Usage: to call the function use DownloaderClass(url,dest) <python> import urllib, os,re,urllib2 import xbmc,xbmcgui

def DownloaderClass(url,dest):

   dp = xbmcgui.DialogProgress()
   dp.create("My Script","Downloading File",url)
   urllib.urlretrieve(url,dest,lambda nb, bs, fs, url=url: _pbhook(nb,bs,fs,url,dp))

def _pbhook(numblocks, blocksize, filesize, url=None,dp=None):

   try:
       percent = min((numblocks*blocksize*100)/filesize, 100)
       print percent
       dp.update(percent)
   except:
       percent = 100
       dp.update(percent)
   if dp.iscanceled(): 
       print "DOWNLOAD CANCELLED" # need to get this part working
       dp.close()
       

url ='http://ftp.iinet.net.au/adsl.test' DownloaderClass(url,"e:\something.txt") </python>

Script Arguments

Passing Arguments to a Script

As of 2007/02/24, arguments can be passed into a script using the builtin command XBMC.RunScript. The first parameter this builtin takes is the absolute location of the python script and all additional parameters are passed as arguments to the script. <python> import os import xbmc

  1. get the parent path of the current script

path = os.getcwd()[:-1]+"\\"

  1. call a different script with the argument 'Hello World'

xbmc.executebuiltin("XBMC.RunScript("+path+"argtest.py,Hello World)") </python>

Using Arguments from sys.argv

The arguments can be accessed from a different script using sys.argv. This is a list of strings that is populated when the script is launched using the builtin command. sys.argv[0] is the name of the script while the rest of the list, sys.argv[1:], are the arguments passed to the script. <python> import xbmcgui import sys

count = len(sys.argv) - 1

if count > 0: xbmcgui.Dialog().ok("Status",sys.argv[0] +" called with " + str(count)+" args", "["+", ".join(sys.argv[1:])+"]") else: xbmcgui.Dialog().ok("Status","no arguments specified")</python>

Script Settings

Script install path

This tips is usefull to let user the opportunity to install the script in the path of their choice. The tricks is to store the path where the script has just been launched <python>

  1. you'll need to import the os library

import os

  1. Then catch the actual path

HOME_DIR=os.getcwd()

  1. the returned path is not correct as it finish with a trailer coma

HOME_DIR=HOME_DIR[:-1] # will delete the last char ; NOTE : with Win OS you'll not delete this trailing char

  1. to be a good path, add double \

HOME_DIR=HOME_DIR+"\\"

  1. all this can be done with a unique line : HOME_DIR=os.getcwd()[:-1]+"\\"
  2. Now we can set every path we need for the script

PICS_DIR=HOME_DIR+"pics\\" DATA_DIR=HOME_DIR+"datas\\"

  1. and when we need to get for example an image, we just use these previous vars as path :

backgroundpic=PICS_DIR+"background_file.png" </python>

Keymapping: How To Map Your Controls In Python

Control Types

There are four Control Types available to map in XBMC:

Controller, Remote, Mouse, & Keyboard


Control Type: Controller

The Controller is one of the two Building_Scripts#Control_TypesControl Type that has two methods you can use to map actions to your Xbox conroller button(s):

Button Codes & Action Codes (see Key.h for a full list of codes)

Action Codes:

Using the Action Codes method is not recommended. Action Codes for the Xbox Controller are limited to the actions available for each button in each XBMC Window. Because of these limitations, some Xbox Controller buttons will not be available (see: Key.h under: "// actions that we have defined..." for an updated list of available Action Codes and their Window limitations).

How to use Action Codes: <python> def onAction(self,action):

   if action == 9:
       self.close()

</python> The above example shows how to assign the Xbox Controller's B Button to close your script when pressed. The number 9 is the assigned Action Code for the B Button (see: Key.h under: "// actions that we have defined..." for an updated list of available Action Codes and their Window limitations).

Button Codes:

The Button Codes method is the recommended method for mapping Xbox Controller buttons. There are no Window limitations with this method and all buttons are completely accessible (see: Controller: Button Code Reference below for a full list of Button Codes).

How to use Button Codes: <python> def onAction(self,action):

   if action.getButtonCode() == 256:
       self.close()

</python> The above example shows how to assign the Xbox Controller's A Button to close your script when pressed. The number 256 is the assigned Button Code for the A Button (see: Controller: Button Code Reference below for a full list of Button Codes).


Control Type: Remote

The Remote is the another Control Type that has two methods you can use to map actions to your Xbox conroller button(s):

Button Codes & Action Codes (see Key.h for a full list of codes)

Button Code Reference

<python>

 Controls                       IDs            Extra Info
 ========                       ===            ==========
 A Button                       256
 B Button                       257
 X Button                       258
 Y Button                       259
 Start Button                   274
 Back Button                    275
 Black Button                   260
 White Button                   261
 Left Trigger Button            262            "Pressing the Left Trigger"
 Left Trigger Analog            278            "Holding down the Left Trigger"
 Right Trigger Button           263            "Pressing the Right Trigger"
 Right Trigger Analog           279            "Holding down the Right Trigger"
 Left ThumbStick                264            "Action is sent when the Left ThumbStick is moved"
 Left ThumbStick Button         276
 Left ThumbStick Up             280
 Left ThumbStick Down           281
 Left ThumbStick Left           282
 Left ThumbStick Right          283
 Right ThumbStick               265            "Action is sent when the Right ThumbStick is moved"
 Right ThumbStick Button        277         
 Right ThumbStick Up            266
 Right ThumbStick Down          267
 Right ThumbStick Left          268
 Right ThumbStick Right         269
 DPad Up                        270
 DPad Down                      271
 DPad Left                      272
 DPad Right                     273

</python>

To obtain an updated list of Action & Button Codes, please see: Key.h