11import os
2- import requests
32import routing
4- import shlex
5- import subprocess
63import sys
7- import time
8- import xbmc
9- import xbmcaddon
10- import xbmcgui
114import xbmcplugin
125import registry
13-
14- from config import *
6+ import steam
7+ from util import *
158
169__addon__ = xbmcaddon .Addon ()
1710
1811plugin = routing .Plugin ()
1912
13+
14+ # Note : the Kodi routing plugin also obtains and casts the handle. We can use it through plugin.handle instead of redefining it.
15+
2016@plugin .route ('/' )
2117def index ():
18+ xbmcplugin .addDirectoryItem (handle = plugin .handle , url = plugin .url_for (all_games ), listitem = xbmcgui .ListItem ('All games' ), isFolder = True )
19+ xbmcplugin .addDirectoryItem (handle = plugin .handle , url = plugin .url_for (installed_games ), listitem = xbmcgui .ListItem ('Installed games' ), isFolder = True )
20+ xbmcplugin .addDirectoryItem (handle = plugin .handle , url = plugin .url_for (recent_games ), listitem = xbmcgui .ListItem ('Recently played games' ), isFolder = True )
21+ xbmcplugin .endOfDirectory (plugin .handle , succeeded = True )
2222
23- handle = int (sys .argv [1 ])
24-
25- xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (all ), listitem = xbmcgui .ListItem ('All games' ), isFolder = True )
26- xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (installed ), listitem = xbmcgui .ListItem ('Installed games' ), isFolder = True )
27- xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (recent ), listitem = xbmcgui .ListItem ('Recently played games' ), isFolder = True )
28- xbmcplugin .endOfDirectory (handle , succeeded = True )
2923
3024@plugin .route ('/all' )
31- def all ():
32-
33- if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
34-
35- # ensure required data is available
25+ def all_games ():
26+ if not all_required_credentials_available ():
3627 return
3728
38- handle = int (sys .argv [1 ])
39-
4029 try :
30+ steam_games_details = steam .get_user_games (__addon__ .getSetting ('steam-key' ), __addon__ .getSetting ('steam-id' ))
4131
42- # query the steam web api for a full list of steam applications/games that belong to the user
43- response = requests .get ('https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=' + __addon__ .getSetting ('steam-key' ) + '&steamid=' + __addon__ .getSetting ('steam-id' ) + '&include_appinfo=1&format=json' , timeout = 10 )
44- response .raise_for_status ()
45-
46- except requests .exceptions .RequestException as e :
47-
32+ except IOError as e :
4833 # something went wrong, can't scan the steam library
49- notify = xbmcgui .Dialog ()
50- notify .notification ('Error' , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. If this problem persists please contact support.' , xbmcgui .NOTIFICATION_ERROR )
51-
52- log (str (e ), xbmc .LOGERROR )
53-
34+ show_error (e , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. '
35+ 'If this problem persists please contact support.' )
5436 return
5537
56- data = response .json ()
57- totalItems = data ['response' ]['game_count' ]
58-
59- for entry in data ['response' ]['games' ]:
38+ directory_items = create_directory_items (steam_games_details )
39+ xbmcplugin .addDirectoryItems (plugin .handle , directory_items )
6040
61- appid = entry [ 'appid' ]
62- name = entry [ 'name' ]
41+ xbmcplugin . addSortMethod ( plugin . handle , xbmcplugin . SORT_METHOD_LABEL )
42+ xbmcplugin . endOfDirectory ( plugin . handle , succeeded = True )
6343
64- item = xbmcgui .ListItem (name )
65-
66- item .addContextMenuItems ([('Play' , 'RunPlugin(plugin://plugin.program.steam.library/run/' + str (appid ) + ')' ), ('Install' , 'RunPlugin(plugin://plugin.program.steam.library/install/' + str (appid ) + ')' )])
67- item .setArt ({ 'thumb' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (appid ) + '/header.jpg' , 'fanart' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (appid ) + '/page_bg_generated_v6b.jpg' })
68-
69- if not xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (run , id = str (appid )), listitem = item , totalItems = totalItems ): break
70-
71- xbmcplugin .addSortMethod (handle , xbmcplugin .SORT_METHOD_LABEL )
72- xbmcplugin .endOfDirectory (handle , succeeded = True )
7344
7445@plugin .route ('/installed' )
75- def installed ():
76-
77- if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
78-
79- # ensure required data is available
46+ def installed_games ():
47+ if not all_required_credentials_available ():
8048 return
8149
82- if os .path .isdir (__addon__ .getSetting ('steam-path' )) == False :
83-
50+ if not os .path .isdir (__addon__ .getSetting ('steam-path' )):
8451 # ensure required data is available
85- notify = xbmcgui .Dialog ()
86- notify .notification ('Error' , 'Unable to find your Steam path, please check your settings.' , xbmcgui .NOTIFICATION_ERROR )
87-
52+ show_error (NameError ("steam-path not found" ), 'Unable to find your Steam path, please check your settings.' )
8853 return
8954
90- handle = int (sys .argv [1 ])
91-
9255 try :
56+ steam_games_details = steam .get_user_games (__addon__ .getSetting ('steam-key' ), __addon__ .getSetting ('steam-id' ))
9357
94- # query the steam web api for a full list of steam applications/games that belong to the user
95- response = requests .get ('https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=' + __addon__ .getSetting ('steam-key' ) + '&steamid=' + __addon__ .getSetting ('steam-id' ) + '&include_appinfo=1&format=json' , timeout = 10 )
96- response .raise_for_status ()
97-
98- except requests .exceptions .RequestException as e :
99-
58+ except IOError as e :
10059 # something went wrong, can't scan the steam library
101- notify = xbmcgui .Dialog ()
102- notify .notification ('Error' , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. If this problem persists please contact support.' , xbmcgui .NOTIFICATION_ERROR )
103-
104- log (str (e ), xbmc .LOGERROR )
105-
60+ show_error (e , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. '
61+ 'If this problem persists please contact support.' )
10662 return
10763
108- apps = registry .get_registry_values (os .path .join (__addon__ .getSetting ('steam-path' ), 'registry.vdf' ))
109- data = response .json ()
110-
111- for entry in data ['response' ]['games' ]:
64+ installed_appids = registry .get_installed_steam_apps (os .path .join (__addon__ .getSetting ('steam-path' ), 'registry.vdf' ))
11265
113- appid = entry [ 'appid' ]
114- name = entry [ 'name' ]
66+ # filter out any applications not listed as installed
67+ steam_installed_games = filter ( lambda app_entry : str ( app_entry [ 'appid' ]) in installed_appids , steam_games_details )
11568
116- # filter out any applications not listed as installed
117- if str (appid ) not in apps : continue
118-
119- item = xbmcgui .ListItem (name )
69+ directory_items = create_directory_items (steam_installed_games )
70+ xbmcplugin .addDirectoryItems (plugin .handle , directory_items )
12071
121- item . addContextMenuItems ([( 'Play' , 'RunPlugin( plugin://plugin.program.steam.library/run/' + str ( appid ) + ')' ), ( 'Install' , 'RunPlugin(plugin://plugin.program.steam.library/install/' + str ( appid ) + ')' )] )
122- item . setArt ({ 'thumb' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str ( appid ) + '/header.jpg' , 'fanart' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str ( appid ) + '/page_bg_generated_v6b.jpg' } )
72+ xbmcplugin . addSortMethod ( plugin . handle , xbmcplugin . SORT_METHOD_LABEL )
73+ xbmcplugin . endOfDirectory ( plugin . handle , succeeded = True )
12374
124- if not xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (run , id = str (appid )), listitem = item ): break
125-
126- xbmcplugin .addSortMethod (handle , xbmcplugin .SORT_METHOD_LABEL )
127- xbmcplugin .endOfDirectory (handle , succeeded = True )
12875
12976@plugin .route ('/recent' )
130- def recent ():
131-
132- if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
133-
134- # ensure required data is available
77+ def recent_games ():
78+ if not all_required_credentials_available ():
13579 return
13680
137- handle = int (sys .argv [1 ])
138-
13981 try :
82+ steam_games_details = steam .get_user_games (__addon__ .getSetting ('steam-key' ), __addon__ .getSetting ('steam-id' ), recent_only = True )
14083
141- # query the steam web api for a full list of steam applications/games that belong to the user
142- response = requests .get ('https://api.steampowered.com/IPlayerService/GetRecentlyPlayedGames/v0001/?key=' + __addon__ .getSetting ('steam-key' ) + '&steamid=' + __addon__ .getSetting ('steam-id' ) + '&include_appinfo=1&format=json' , timeout = 10 )
143- response .raise_for_status ()
144-
145- except requests .exceptions .RequestException as e :
146-
84+ except IOError as e :
14785 # something went wrong, can't scan the steam library
148- notify = xbmcgui .Dialog ()
149- notify .notification ('Error' , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. If this problem persists please contact support.' , xbmcgui .NOTIFICATION_ERROR )
150-
151- log (str (e ), xbmc .LOGERROR )
152-
86+ show_error (e , 'An unexpected error has occurred while contacting Steam. Please ensure your Steam credentials are correct and then try again. '
87+ 'If this problem persists please contact support.' )
15388 return
15489
155- data = response . json ( )
156- totalItems = data [ 'response' ][ 'total_count' ]
90+ directory_items = create_directory_items ( steam_games_details )
91+ xbmcplugin . addDirectoryItems ( plugin . handle , directory_items )
15792
158- for entry in data ['response' ]['games' ]:
93+ xbmcplugin .addSortMethod (plugin .handle , xbmcplugin .SORT_METHOD_UNSORTED )
94+ xbmcplugin .endOfDirectory (plugin .handle , succeeded = True )
15995
160- appid = entry ['appid' ]
161- name = entry ['name' ]
16296
163- item = xbmcgui .ListItem (name )
164- item .setArt ({ 'thumb' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (appid ) + '/header.jpg' , 'fanart' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (appid ) + '/page_bg_generated_v6b.jpg' })
97+ @plugin .route ('/install/<appid>' )
98+ def install (appid ):
99+ if not os .path .isfile (__addon__ .getSetting ('steam-exe' )):
100+ # ensure required data is available
101+ show_error (NameError ('steam-exe not found' ), 'Unable to find your Steam executable, please check your settings.' )
102+ return
165103
166- if not xbmcplugin . addDirectoryItem ( handle = handle , url = plugin . url_for ( run , id = str ( appid )), listitem = item , totalItems = totalItems ): break
104+ steam . install ( __addon__ . getSetting ( 'steam-exe' ), appid )
167105
168- xbmcplugin .addSortMethod (handle , xbmcplugin .SORT_METHOD_UNSORTED )
169- xbmcplugin .endOfDirectory (handle , succeeded = True )
170106
171- @plugin .route ('/install/<id>' )
172- def install (id ):
107+ @plugin .route ('/run/<appid>' )
108+ def run (appid ):
109+ if not os .path .isfile (__addon__ .getSetting ('steam-exe' )):
110+ # ensure required data is available
111+ show_error (NameError ('steam-exe not found' ), 'Unable to find your Steam executable, please check your settings.' )
112+ return
173113
174- if os . path . isfile (__addon__ .getSetting ('steam-exe' )) == False :
114+ steam . run (__addon__ .getSetting ('steam-exe' ), __addon__ . getSetting ( 'steam-args' ), appid )
175115
176- # ensure required data is available
177- notify = xbmcgui .Dialog ()
178- notify .notification ('Error' , 'Unable to find your Steam executable, please check your settings.' , xbmcgui .NOTIFICATION_ERROR )
179116
180- return
117+ def create_directory_items (app_entries ):
118+ """
119+ Creates a list item for each game/app entry provided
181120
182- log ('executing ' + __addon__ .getSetting ('steam-exe' ) + ' steam://install/' + id )
121+ :param app_entries: array of game entries, with each entry containing at least keys : appid, name, img_icon_url, img_logo_url, playtime_forever
122+ :returns: an array of list items of the game entries, formatted like so : [(url,listItem,bool),..]
123+ """
124+ directory_items = []
125+ for app_entry in app_entries :
126+ appid = str (app_entry ['appid' ])
127+ name = app_entry ['name' ]
183128
184- # https://developer.valvesoftware.com/wiki/Steam_browser_protocol
185- subprocess . call ([ __addon__ . getSetting ( 'steam-exe' ), 'steam://install/' + id ] )
129+ run_url = plugin . url_for ( run , appid = appid )
130+ item = xbmcgui . ListItem ( name )
186131
187- @ plugin . route ( '/run/<id>' )
188- def run ( id ):
132+ item . addContextMenuItems ([( 'Play' , 'RunPlugin(' + run_url + ')' ),
133+ ( 'Install' , 'RunPlugin(' + plugin . url_for ( install , appid = appid ) + ')' )])
189134
190- if os .path .isfile (__addon__ .getSetting ('steam-exe' )) == False :
135+ art_dictionary = create_arts_dictionary (app_entry )
136+ item .setArt (art_dictionary )
191137
192- # ensure required data is available
193- notify = xbmcgui .Dialog ()
194- notify .notification ('Error' , 'Unable to find your Steam executable, please check your settings.' , xbmcgui .NOTIFICATION_ERROR )
138+ directory_items .append ((run_url , item , False ))
195139
196- return
140+ return directory_items
197141
198- userArgs = shlex .split (__addon__ .getSetting ('steam-args' ))
199142
200- log ('executing ' + __addon__ .getSetting ('steam-exe' ) + ' ' + __addon__ .getSetting ('steam-args' ) + ' steam://rungameid/' + id )
143+ def create_arts_dictionary (app_entry ):
144+ """
145+ Creates a dictionary of arts keys and their associated links, for a given app entry.
146+ :param app_entry: dictionary of app informations, containing at least the keys : appid, img_icon_url, img_logo_url
147+ :return: dictionary of arts for the app.
148+ """
149+ art_dictionary = {'thumb' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (app_entry ['appid' ]) + '/header.jpg' ,
150+ 'fanart' : 'http://cdn.akamai.steamstatic.com/steam/apps/' + str (app_entry ['appid' ]) + '/page_bg_generated_v6b.jpg' }
151+ return art_dictionary
201152
202- # https://developer.valvesoftware.com/wiki/Steam_browser_protocol
203- subprocess .call ([__addon__ .getSetting ('steam-exe' )] + userArgs + ['steam://rungameid/' + id ])
204153
205154def main ():
206-
207155 log ('steam-id = ' + __addon__ .getSetting ('steam-id' ))
208156 log ('steam-key = ' + __addon__ .getSetting ('steam-key' ))
209157 log ('steam-exe = ' + __addon__ .getSetting ('steam-exe' ))
210158 log ('steam-path = ' + __addon__ .getSetting ('steam-path' ))
211159
212160 # backwards compatibility for versions prior to 0.6.0
213- if __addon__ .getSetting ('steam-id' ) != '' and __addon__ .getSetting ('steam-key' ) != '' and __addon__ .getSetting ('steam-path' ) != '' and __addon__ .getSetting ('steam-exe' ) == '' :
161+ if __addon__ .getSetting ('steam-id' ) != '' and __addon__ .getSetting ('steam-key' ) != '' \
162+ and __addon__ .getSetting ('steam-path' ) != '' and __addon__ .getSetting ('steam-exe' ) == '' :
214163
215- __addon__ .setSetting ('steam-exe' , __addon__ .getSetting ('steam-path' ));
164+ __addon__ .setSetting ('steam-exe' , __addon__ .getSetting ('steam-path' ))
216165
217166 if sys .platform == "linux" or sys .platform == "linux2" :
218167
219- __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ));
168+ __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ))
220169
221170 elif sys .platform == "win32" :
222171
@@ -233,7 +182,7 @@ def main():
233182 if sys .platform == "linux" or sys .platform == "linux2" :
234183
235184 __addon__ .setSetting ('steam-exe' , '/usr/bin/steam' )
236- __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ));
185+ __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ))
237186
238187 elif sys .platform == "darwin" :
239188
@@ -251,13 +200,11 @@ def main():
251200 __addon__ .setSetting ('steam-path' , os .path .expandvars ('%ProgramFiles(x86)%\\ Steam\\ Steam.exe' ))
252201
253202 if __addon__ .getSetting ('version' ) == '' :
254-
255203 # first time run, store version
256- __addon__ .setSetting ('version' , '0.6.0' );
204+ __addon__ .setSetting ('version' , '0.6.0' )
257205
258206 # prompt the user to configure the plugin with their steam details
259- if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
260-
207+ if not all_required_credentials_available ():
261208 __addon__ .openSettings ()
262209
263210 plugin .run ()
0 commit comments