@@ -21,12 +21,43 @@ def log(msg, level=xbmc.LOGDEBUG):
2121
2222 xbmc .log ('[%s] %s' % (__addon__ .getAddonInfo ('id' ), msg ), level = level )
2323
24+ # https://github.com/lutris/lutris/blob/master/lutris/util/steam.py
25+ def vdf_parse (steam_config_file , config ):
26+ """Parse a Steam config file and return the contents as a dict."""
27+ line = " "
28+ while line :
29+ try :
30+ line = steam_config_file .readline ()
31+ except UnicodeDecodeError :
32+ log ("Error while reading Steam VDF file {}. Returning {}" .format (steam_config_file , config ), xbmc .LOGERROR )
33+ return config
34+ if not line or line .strip () == "}" :
35+ return config
36+ while not line .strip ().endswith ("\" " ):
37+ nextline = steam_config_file .readline ()
38+ if not nextline :
39+ break
40+ line = line [:- 1 ] + nextline
41+
42+ line_elements = line .strip ().split ("\" " )
43+ if len (line_elements ) == 3 :
44+ key = line_elements [1 ]
45+ steam_config_file .readline () # skip '{'
46+ config [key ] = vdf_parse (steam_config_file , {})
47+ else :
48+ try :
49+ config [line_elements [1 ]] = line_elements [3 ]
50+ except IndexError :
51+ log ('Malformed config file: {}' .format (line ), xbmc .LOGERROR )
52+ return config
53+
2454@plugin .route ('/' )
2555def index ():
2656
2757 handle = int (sys .argv [1 ])
2858
2959 xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (all ), listitem = xbmcgui .ListItem ('All games' ), isFolder = True )
60+ xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (installed ), listitem = xbmcgui .ListItem ('Installed games' ), isFolder = True )
3061 xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (recent ), listitem = xbmcgui .ListItem ('Recently played games' ), isFolder = True )
3162 xbmcplugin .endOfDirectory (handle , succeeded = True )
3263
@@ -74,6 +105,65 @@ def all():
74105 xbmcplugin .addSortMethod (handle , xbmcplugin .SORT_METHOD_LABEL )
75106 xbmcplugin .endOfDirectory (handle , succeeded = True )
76107
108+ @plugin .route ('/installed' )
109+ def installed ():
110+
111+ if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
112+
113+ # ensure required data is available
114+ return
115+
116+ if os .path .isdir (__addon__ .getSetting ('steam-path' )) == False :
117+
118+ # ensure required data is available
119+ notify = xbmcgui .Dialog ()
120+ notify .notification ('Error' , 'Unable to find your Steam path, please check your settings.' , xbmcgui .NOTIFICATION_ERROR )
121+
122+ return
123+
124+ handle = int (sys .argv [1 ])
125+
126+ try :
127+
128+ # query the steam web api for a full list of steam applications/games that belong to the user
129+ 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 )
130+ response .raise_for_status ()
131+
132+ except requests .exceptions .RequestException as e :
133+
134+ # something went wrong, can't scan the steam library
135+ notify = xbmcgui .Dialog ()
136+ 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 )
137+
138+ log (str (e ), xbmc .LOGERROR )
139+
140+ return
141+
142+ with open (os .path .join (__addon__ .getSetting ('steam-path' ), 'registry.vdf' ), 'r' ) as file :
143+ vdf = vdf_parse (file , {})
144+ apps = vdf ['Registry' ]['HKCU' ]['Software' ]['Valve' ]['Steam' ]['Apps' ]
145+ apps = dict ((k , v ) for (k , v ) in apps .iteritems () if v .get ('installed' , '0' ) == '1' )
146+
147+ data = response .json ()
148+
149+ for entry in data ['response' ]['games' ]:
150+
151+ appid = entry ['appid' ]
152+ name = entry ['name' ]
153+
154+ # filter out any applications not listed as installed
155+ if str (appid ) not in apps : continue
156+
157+ item = xbmcgui .ListItem (name )
158+
159+ item .addContextMenuItems ([('Play' , 'RunPlugin(plugin://plugin.program.steam.library/run/' + str (appid ) + ')' ), ('Install' , 'RunPlugin(plugin://plugin.program.steam.library/install/' + str (appid ) + ')' )])
160+ 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' })
161+
162+ if not xbmcplugin .addDirectoryItem (handle = handle , url = plugin .url_for (run , id = str (appid )), listitem = item ): break
163+
164+ xbmcplugin .addSortMethod (handle , xbmcplugin .SORT_METHOD_LABEL )
165+ xbmcplugin .endOfDirectory (handle , succeeded = True )
166+
77167@plugin .route ('/recent' )
78168def recent ():
79169
@@ -119,23 +209,23 @@ def recent():
119209@plugin .route ('/install/<id>' )
120210def install (id ):
121211
122- if os .path .isfile (__addon__ .getSetting ('steam-path ' )) == False :
212+ if os .path .isfile (__addon__ .getSetting ('steam-exe ' )) == False :
123213
124214 # ensure required data is available
125215 notify = xbmcgui .Dialog ()
126216 notify .notification ('Error' , 'Unable to find your Steam executable, please check your settings.' , xbmcgui .NOTIFICATION_ERROR )
127217
128218 return
129219
130- log ('executing ' + __addon__ .getSetting ('steam-path ' ) + ' steam://install/' + id )
220+ log ('executing ' + __addon__ .getSetting ('steam-exe ' ) + ' steam://install/' + id )
131221
132222 # https://developer.valvesoftware.com/wiki/Steam_browser_protocol
133- subprocess .call ([__addon__ .getSetting ('steam-path ' ), 'steam://install/' + id ])
223+ subprocess .call ([__addon__ .getSetting ('steam-exe ' ), 'steam://install/' + id ])
134224
135225@plugin .route ('/run/<id>' )
136226def run (id ):
137227
138- if os .path .isfile (__addon__ .getSetting ('steam-path ' )) == False :
228+ if os .path .isfile (__addon__ .getSetting ('steam-exe ' )) == False :
139229
140230 # ensure required data is available
141231 notify = xbmcgui .Dialog ()
@@ -145,37 +235,64 @@ def run(id):
145235
146236 userArgs = shlex .split (__addon__ .getSetting ('steam-args' ))
147237
148- log ('executing ' + __addon__ .getSetting ('steam-path ' ) + ' ' + __addon__ .getSetting ('steam-args' ) + ' steam://rungameid/' + id )
238+ log ('executing ' + __addon__ .getSetting ('steam-exe ' ) + ' ' + __addon__ .getSetting ('steam-args' ) + ' steam://rungameid/' + id )
149239
150240 # https://developer.valvesoftware.com/wiki/Steam_browser_protocol
151- subprocess .call ([__addon__ .getSetting ('steam-path ' )] + userArgs + ['steam://rungameid/' + id ])
241+ subprocess .call ([__addon__ .getSetting ('steam-exe ' )] + userArgs + ['steam://rungameid/' + id ])
152242
153243def main ():
154244
155245 log ('steam-id = ' + __addon__ .getSetting ('steam-id' ))
156246 log ('steam-key = ' + __addon__ .getSetting ('steam-key' ))
247+ log ('steam-exe = ' + __addon__ .getSetting ('steam-exe' ))
157248 log ('steam-path = ' + __addon__ .getSetting ('steam-path' ))
158249
250+ # backwards compatibility for versions prior to 0.6.0
251+ if __addon__ .getSetting ('steam-id' ) != '' and __addon__ .getSetting ('steam-key' ) != '' and __addon__ .getSetting ('steam-path' ) != '' and __addon__ .getSetting ('steam-exe' ) == '' :
252+
253+ __addon__ .setSetting ('steam-exe' , __addon__ .getSetting ('steam-path' ));
254+
255+ if sys .platform == "linux" or sys .platform == "linux2" :
256+
257+ __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ));
258+
259+ elif sys .platform == "win32" :
260+
261+ __addon__ .setSetting ('steam-path' , os .path .expandvars ('%ProgramFiles%\\ Steam\\ Steam.exe' ))
262+
263+ elif sys .platform == "win64" :
264+
265+ __addon__ .setSetting ('steam-path' , os .path .expandvars ('%ProgramFiles(x86)%\\ Steam\\ Steam.exe' ))
266+
159267 # all settings are empty, assume this is the first run
160268 # best guess at steam executable path
161- if __addon__ .getSetting ('steam-id' ) == '' and __addon__ .getSetting ('steam-key' ) == '' and __addon__ .getSetting ('steam-path ' ) == '' :
269+ if __addon__ .getSetting ('steam-id' ) == '' and __addon__ .getSetting ('steam-key' ) == '' and __addon__ .getSetting ('steam-exe ' ) == '' :
162270
163271 if sys .platform == "linux" or sys .platform == "linux2" :
164272
165- __addon__ .setSetting ('steam-path' , '/usr/bin/steam' )
273+ __addon__ .setSetting ('steam-exe' , '/usr/bin/steam' )
274+ __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/.steam' ));
166275
167276 elif sys .platform == "darwin" :
168277
169- __addon__ .setSetting ('steam-path' , os .path .expanduser ('~/Library/Application Support/Steam/Steam.app/Contents/MacOS/steam_osx' ))
278+ __addon__ .setSetting ('steam-exe' , os .path .expanduser ('~/Library/Application Support/Steam/Steam.app/Contents/MacOS/steam_osx' ))
279+ # TODO: not a clue
170280
171281 elif sys .platform == "win32" :
172282
283+ __addon__ .setSetting ('steam-exe' , os .path .expandvars ('%ProgramFiles%\\ Steam\\ Steam.exe' ))
173284 __addon__ .setSetting ('steam-path' , os .path .expandvars ('%ProgramFiles%\\ Steam\\ Steam.exe' ))
174285
175286 elif sys .platform == "win64" :
176287
288+ __addon__ .setSetting ('steam-exe' , os .path .expandvars ('%ProgramFiles(x86)%\\ Steam\\ Steam.exe' ))
177289 __addon__ .setSetting ('steam-path' , os .path .expandvars ('%ProgramFiles(x86)%\\ Steam\\ Steam.exe' ))
178290
291+ if __addon__ .getSetting ('version' ) == '' :
292+
293+ # first time run, store version
294+ __addon__ .setSetting ('version' , '0.6.0' );
295+
179296 # prompt the user to configure the plugin with their steam details
180297 if __addon__ .getSetting ('steam-id' ) == '' or __addon__ .getSetting ('steam-key' ) == '' :
181298
0 commit comments