def __audioSuspend(self): if __addon__.getSetting( util.SETTING_RCB_SUSPENDAUDIO).upper() == 'TRUE': log.debug("Suspending audio") xbmc.executebuiltin("PlayerControl(Stop)") xbmc.enableNavSounds(False) xbmc.audioSuspend()
def insertGame(self, game_row, isUpdate, gameId, allowUpdate): #HACK: delete first element as we do not want to insert or update the id del game_row[0] # Check if exists and insert/update as appropriate; move this functionality to the Game object try: if not isUpdate: log.info(u"Game does not exist in database. Insert game: %s" % game_row[DataBaseObject.COL_NAME]) Game(self.gdb).insert(game_row) return self.gdb.cursor.lastrowid else: if allowUpdate: # check if we are allowed to update with null values allowOverwriteWithNullvalues = __addon__.getSetting( util.SETTING_RCB_ALLOWOVERWRITEWITHNULLVALUES).upper() == 'TRUE' log.info("allowOverwriteWithNullvalues: {0}".format(allowOverwriteWithNullvalues)) log.info(u"Game does exist in database. Update game: %s" % game_row[DataBaseObject.COL_NAME]) #remove id from column list columns = Game.FIELDNAMES[1:] Game(self.gdb).update(columns, game_row, gameId, allowOverwriteWithNullvalues) else: log.info( u"Game does exist in database but update is not allowed for current rom collection. game: %s" % game_row[DataBaseObject.COL_NAME]) return gameId except Exception as exc: log.error(u"An error occured while adding game '%s'. Error: %s" % (game_row[DataBaseObject.COL_NAME], exc)) return None
def __copyLauncherScriptsToUserdata(self): log.info('__copyLauncherScriptsToUserdata') oldBasePath = os.path.join(util.getAddonInstallPath(), 'resources', 'scriptfiles') newBasePath = os.path.join(util.getAddonDataPath(), 'scriptfiles') files = [] # Copy applaunch shell script/batch file if self.env == 'win32': files.append('applaunch.bat') else: files.append('applaunch.sh') # Copy VBS files if self.env == 'win32' and __addon__.getSetting( util.SETTING_RCB_USEVBINSOLOMODE).lower() == 'true': files += ['applaunch-vbs.bat', 'LaunchKodi.vbs', 'Sleep.vbs'] for f in files: if not xbmcvfs.exists(os.path.join(newBasePath, f)): log.debug("Copying file {0} from {1} to {2}".format( f, oldBasePath, newBasePath)) if not xbmcvfs.copy(os.path.join(oldBasePath, f), os.path.join(newBasePath, f)): log.warn("Error copying file")
def prepare_solomode(self, romCollection, cmd): # solo mode if romCollection.useEmuSolo: self.__copyLauncherScriptsToUserdata() # communicate with service via settings __addon__.setSetting(util.SETTING_RCB_LAUNCHONSTARTUP, 'true') # invoke script file that kills xbmc before launching the emulator basePath = os.path.join(util.getAddonDataPath(), 'scriptfiles') if self.env == "win32": if __addon__.getSetting( util.SETTING_RCB_USEVBINSOLOMODE).lower() == 'true': # There is a problem with quotes passed as argument to windows command shell. This only works with "call" # use vb script to restart xbmc cmd = 'call \"' + os.path.join( basePath, 'applaunch-vbs.bat') + '\" ' + cmd else: # There is a problem with quotes passed as argument to windows command shell. This only works with "call" cmd = 'call \"' + os.path.join( basePath, 'applaunch.bat') + '\" ' + cmd else: cmd = os.path.join(basePath, 'applaunch.sh ') + cmd else: # use call to support paths with whitespaces if self.env == "win32": cmd = 'call ' + cmd return cmd
def insertGameFromDesc(self, gamedescription, gamename, romCollection, filenamelist, foldername, isUpdate, gameId): log.info("insertGameFromDesc") if gamedescription is not None: game = self.resolveParseResult(gamedescription, 'Game') else: game = '' # if no game name has been scraped we expect that no results have been found if game == '': self.missingDescFile.add_entry(gamename) if __addon__.getSetting( util.SETTING_RCB_IGNOREGAMEWITHOUTDESC).upper() == 'TRUE': log.warn( "No description found for game '%s'. Game will not be imported." % gamename) return None gamedescription = {} gameId = self.insertData(gamedescription, gamename, romCollection, filenamelist, foldername, isUpdate, gameId) return gameId
def __init__(self): self.env = (os.environ.get("OS", "win32"), "win32", )[os.environ.get("OS", "win32") == "xbox"] log.debug("Running environment detected as {0}".format(self.env)) # Do we need to escape commands before executing? self.escapeCmd = __addon__.getSetting(util.SETTING_RCB_ESCAPECOMMAND).upper() == 'TRUE' self.romCollection = None
def __init__(self): self.env = (os.environ.get("OS", "win32"), "win32",)[os.environ.get("OS", "win32") == "xbox"] log.debug("Running environment detected as {0}".format(self.env)) # Do we need to escape commands before executing? self.escapeCmd = __addon__.getSetting(util.SETTING_RCB_ESCAPECOMMAND).upper() == 'TRUE' self.romCollection = None
def __init__(self): # Do we need to escape commands before executing? self.escapeCmd = __addon__.getSetting( util.SETTING_RCB_ESCAPECOMMAND).upper() == 'TRUE' self.env = ( os.environ.get("OS", "win32"), "win32", )[os.environ.get("OS", "win32") == "xbox"] self.screenModeToggled = False
def insertGameFromDesc(self, gamedescription, gamename_from_file, romCollection, romfiles, foldername, isUpdate, gameId): log.info("insertGameFromDesc") if gamedescription is not None: game = self.resolveParseResult(gamedescription, 'Game') else: game = '' # if no game name has been scraped we expect that no results have been found if game == '': self.missingDescFile.add_entry(gamename_from_file) if __addon__.getSetting(util.SETTING_RCB_IGNOREGAMEWITHOUTDESC).upper() == 'TRUE': log.warn("No description found for game '%s'. Game will not be imported." % gamename_from_file) return None gamedescription = {} game_row = self.convert_parseresult_to_gamerow(gamedescription, gamename_from_file) game_row[Game.COL_romCollectionId] = romCollection.id gamename = game_row[Game.COL_NAME] publisher = self.resolveParseResult(gamedescription, 'Publisher') developer = self.resolveParseResult(gamedescription, 'Developer') artWorkFound, artworkfiles, artworkurls = self.getArtworkForGame(romCollection, gamename, gamename_from_file, gamedescription, foldername, publisher, developer) #add artwork filenames to game_row for filetype, filenames in list(artworkfiles.items()): for filename in filenames: prop = 'COL_fileType%s' % filetype.id index = getattr(Game, prop) game_row[index] = filename gameId = self.insertGame(game_row, isUpdate, gameId, romCollection.allowUpdate, ) if gameId is None: return None genreIds = self.insertForeignKeyItemList(gamedescription, 'Genre', Genre(self.gdb)) self.add_genres_to_db(genreIds, gameId) self.add_romfiles_to_db(romfiles, gameId) self.gdb.commit() # Create Nfo file with game properties try: # Read game from db as nfowriter expects GameView db row gamerow = GameView(self.gdb).getGameById(gameId) writer = NfoWriter() writer.createNfoFromDesc(gamerow, romCollection.name, romfiles[0], gamename_from_file, artworkfiles, artworkurls) except Exception as e: log.warn(u"Unable to write NFO file for game %s: %s" % (gamename, e)) return gameId
def getScrapingMode(self): mode = 0 scrape_options = {util.SCRAPING_OPTION_AUTO_ACCURATE_TXT: 0, util.SCRAPING_OPTION_INTERACTIVE_TXT: 1} try: mode = scrape_options[__addon__.getSetting(util.SETTING_RCB_SCRAPINGMODE)] except KeyError: pass log.info("Scraping mode: {0}".format(mode)) return mode
def __init__(self, gdb, config, gui): self.gdb = gdb self.config = config self.gui = gui # Do we need to escape commands before executing? self.escapeCmd = __addon__.getSetting( util.SETTING_RCB_ESCAPECOMMAND).upper() == 'TRUE' self.env = ( os.environ.get("OS", "win32"), "win32", )[os.environ.get("OS", "win32") == "xbox"]
def insertGame(self, gameName, description, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, originalTitle, alternateTitle, translatedBy, version, isFavorite, launchCount, isUpdate, gameId, allowUpdate): # Check if exists and insert/update as appropriate; move this functionality to the Game object try: if not isUpdate: log.info(u"Game does not exist in database. Insert game: %s" % gameName) Game(self.gdb).insert( (gameName, description, None, None, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, int(isFavorite), int(launchCount), originalTitle, alternateTitle, translatedBy, version)) return self.gdb.cursor.lastrowid else: if allowUpdate: # check if we are allowed to update with null values allowOverwriteWithNullvalues = __addon__.getSetting( util.SETTING_RCB_ALLOWOVERWRITEWITHNULLVALUES).upper( ) == 'TRUE' log.info("allowOverwriteWithNullvalues: {0}".format( allowOverwriteWithNullvalues)) log.info(u"Game does exist in database. Update game: %s" % gameName) Game(self.gdb).update( ('name', 'description', 'romCollectionId', 'publisherId', 'developerId', 'reviewerId', 'yearId', 'maxPlayers', 'rating', 'numVotes', 'url', 'region', 'media', 'perspective', 'controllerType', 'originalTitle', 'alternateTitle', 'translatedBy', 'version', 'isFavorite', 'launchCount'), (gameName, description, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, originalTitle, alternateTitle, translatedBy, version, int(isFavorite), int(launchCount)), gameId, allowOverwriteWithNullvalues) else: log.info( u"Game does exist in database but update is not allowed for current rom collection. game: %s" % gameName) return gameId except Exception, (exc): log.error(u"An error occured while adding game '%s'. Error: %s" % (gameName, exc)) return None
def __toggle_screenmode(self): screenMode = xbmc.getInfoLabel("System.Screenmode") log.info("screenMode: " + screenMode) isFullScreen = screenMode.endswith("Full Screen") toggleScreenMode = __addon__.getSetting( util.SETTING_RCB_TOGGLESCREENMODE).upper() == 'TRUE' if isFullScreen and toggleScreenMode: log.info("Toggling to windowed mode") # this minimizes xbmc some apps seems to need it xbmc.executeJSONRPC(KODI_JSONRPC_TOGGLE_FULLSCREEN) self.screenModeToggled = True
def __enableScreensaver(self): if __addon__.getSetting( util.SETTING_RCB_DISABLESCREENSAVER).upper() == 'TRUE': log.debug("Enable Screensaver") screensaver = __addon__.getSetting( util.SETTING_RCB_CURRENTSCREENSAVER) log.debug("Set Screensaver back to: {0}".format(screensaver)) jsonobj = { "jsonrpc": "2.0", "id": 0, "method": "Settings.setSettingValue", "params": { "setting": "screensaver.mode", "value": screensaver } } xbmc.executeJSONRPC(json.dumps(jsonobj)) xbmc.sleep(2000) response = xbmc.executeJSONRPC( '{"jsonrpc": "2.0", "method": "XBMC.GetInfoBooleans", "params": {"booleans": ["System.ScreenSaverActive"]}, "id": 1}' ) log.debug('Screensaver is active: {0}'.format(response)) jsonresult = json.loads(response) screensaver_active = jsonresult['result'][ 'System.ScreenSaverActive'] log.debug('Screensaver is active: {0}'.format(screensaver_active)) if (screensaver_active): xbmc.executeJSONRPC( '{"jsonrpc": "2.0", "method": "Input.Select", "id": 1}')
def getScrapingMode(self): mode = 0 scrape_options = { util.SCRAPING_OPTION_AUTO_ACCURATE_TXT: 0, util.SCRAPING_OPTION_INTERACTIVE_TXT: 1 } try: mode = scrape_options[__addon__.getSetting( util.SETTING_RCB_SCRAPINGMODE)] except KeyError: pass log.info("Scraping mode: {0}".format(mode)) return mode
def __disableScreensaver(self): if __addon__.getSetting( util.SETTING_RCB_DISABLESCREENSAVER).upper() == 'TRUE': log.debug("Disable Screensaver") response = xbmc.executeJSONRPC( '{ "jsonrpc": "2.0", "id": 0, "method": "Settings.getSettingValue", "params": {"setting":"screensaver.mode" } }' ) jsonresult = json.loads(response) screensaver = jsonresult['result']['value'] log.debug("Current Screensaver: {0}".format(screensaver)) __addon__.setSetting(util.SETTING_RCB_CURRENTSCREENSAVER, screensaver) log.debug("Set Screensaver to empty value") xbmc.executeJSONRPC( '{ "jsonrpc": "2.0", "id": 0, "method":"Settings.setSettingValue", "params": {"setting":"screensaver.mode", "value":""} } ' )
def insertGameFromDesc(self, gamedescription, gamename, romCollection, filenamelist, foldername, isUpdate, gameId): log.info("insertGameFromDesc") if gamedescription is not None: game = self.resolveParseResult(gamedescription, 'Game') else: game = '' # if no game name has been scraped we expect that no results have been found if game == '': self.missingDescFile.add_entry(gamename) if __addon__.getSetting(util.SETTING_RCB_IGNOREGAMEWITHOUTDESC).upper() == 'TRUE': log.warn("No description found for game '%s'. Game will not be imported." % gamename) return None gamedescription = {} gameId = self.insertData(gamedescription, gamename, romCollection, filenamelist, foldername, isUpdate, gameId) return gameId
def __copyLauncherScriptsToUserdata(self): log.info('__copyLauncherScriptsToUserdata') oldBasePath = os.path.join(util.getAddonInstallPath(), 'resources', 'scriptfiles') newBasePath = os.path.join(util.getAddonDataPath(), 'scriptfiles') files = [] # Copy applaunch shell script/batch file if self.env == 'win32': files.append('applaunch.bat') else: files.append('applaunch.sh') # Copy VBS files if self.env == 'win32' and __addon__.getSetting(util.SETTING_RCB_USEVBINSOLOMODE).lower() == 'true': files += ['applaunch-vbs.bat', 'LaunchKodi.vbs', 'Sleep.vbs'] for f in files: if not xbmcvfs.exists(os.path.join(newBasePath, f)): log.debug("Copying file {0} from {1} to {2}".format(f, oldBasePath, newBasePath)) if not xbmcvfs.copy(os.path.join(oldBasePath, f), os.path.join(newBasePath, f)): log.warn("Error copying file")
def insertGame(self, gameName, description, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, originalTitle, alternateTitle, translatedBy, version, isFavorite, launchCount, isUpdate, gameId, allowUpdate): # Check if exists and insert/update as appropriate; move this functionality to the Game object try: if not isUpdate: log.info(u"Game does not exist in database. Insert game: %s" % gameName) Game(self.gdb).insert( (gameName, description, None, None, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, int(isFavorite), int(launchCount), originalTitle, alternateTitle, translatedBy, version)) return self.gdb.cursor.lastrowid else: if allowUpdate: # check if we are allowed to update with null values allowOverwriteWithNullvalues = __addon__.getSetting( util.SETTING_RCB_ALLOWOVERWRITEWITHNULLVALUES).upper() == 'TRUE' log.info("allowOverwriteWithNullvalues: {0}".format(allowOverwriteWithNullvalues)) log.info(u"Game does exist in database. Update game: %s" % gameName) Game(self.gdb).update(('name', 'description', 'romCollectionId', 'publisherId', 'developerId', 'reviewerId', 'yearId', 'maxPlayers', 'rating', 'numVotes', 'url', 'region', 'media', 'perspective', 'controllerType', 'originalTitle', 'alternateTitle', 'translatedBy', 'version', 'isFavorite', 'launchCount'), (gameName, description, romCollectionId, publisherId, developerId, reviewerId, yearId, players, rating, votes, url, region, media, perspective, controller, originalTitle, alternateTitle, translatedBy, version, int(isFavorite), int(launchCount)), gameId, allowOverwriteWithNullvalues) else: log.info( u"Game does exist in database but update is not allowed for current rom collection. game: %s" % gameName) return gameId except Exception, (exc): log.error(u"An error occured while adding game '%s'. Error: %s" % (gameName, exc)) return None
def launchEmu(self, gdb, gui, gameId, config, listitem): log.info("Begin launcher.launchEmu") gameRow = Game(gdb).getObjectById(gameId) if gameRow is None: log.error("Game with id %s could not be found in database" % gameId) return try: self.romCollection = config.romCollections[str(gameRow[util.GAME_romCollectionId])] except KeyError: log.error("Cannot get rom collection with id: " + str(gameRow[util.GAME_romCollectionId])) gui.writeMsg(util.localize(32034)) return gui.writeMsg(util.localize(32163) + " " + gameRow[util.ROW_NAME]) # Remember viewstate gui.saveViewState(False) cmd = "" precmd = "" postcmd = "" filenameRows = File(gdb).getRomsByGameId(gameRow[util.ROW_ID]) log.info("files for current game: " + str(filenameRows)) cmd, precmd, postcmd, roms = self.__buildCmd(gui, filenameRows, gameRow, False) if not self.romCollection.useBuiltinEmulator: if cmd == '': log.info("No cmd created. Game will not be launched.") return if precmd.strip() == '' or precmd.strip() == 'call': log.info("No precmd created.") if postcmd.strip() == '' or postcmd.strip() == 'call': log.info("No postcmd created.") # solo mode if self.romCollection.useEmuSolo: self.__copyLauncherScriptsToUserdata() # communicate with service via settings __addon__.setSetting(util.SETTING_RCB_LAUNCHONSTARTUP, 'true') # invoke script file that kills xbmc before launching the emulator basePath = os.path.join(util.getAddonDataPath(), 'scriptfiles') if self.env == "win32": if __addon__.getSetting(util.SETTING_RCB_USEVBINSOLOMODE).lower() == 'true': # There is a problem with quotes passed as argument to windows command shell. This only works with "call" # use vb script to restart xbmc cmd = 'call \"' + os.path.join(basePath, 'applaunch-vbs.bat') + '\" ' + cmd else: # There is a problem with quotes passed as argument to windows command shell. This only works with "call" cmd = 'call \"' + os.path.join(basePath, 'applaunch.bat') + '\" ' + cmd else: cmd = os.path.join(basePath, 'applaunch.sh ') + cmd else: # use call to support paths with whitespaces if self.env == "win32": cmd = 'call ' + cmd # update LaunchCount launchCount = gameRow[util.GAME_launchCount] Game(gdb).update(('launchCount',), (launchCount + 1,), gameRow[util.ROW_ID], True) gdb.commit() log.info("cmd: " + cmd) log.info("precmd: " + precmd) log.info("postcmd: " + postcmd) try: self.__launchNonXbox(cmd, gameRow, precmd, postcmd, roms, gui, listitem) gui.writeMsg("") except Exception, (exc): log.error("Error while launching emu: " + str(exc)) gui.writeMsg(util.localize(32035) + ": " + str(exc))
def updateDB(self, gdb, gui, romCollections, isRescrape): self.gdb = gdb self._gui = gui log.info("Start Update DB") #at start, check if we need to create any artwork directories if not helper.createArtworkDirectories(romCollections): #32010: Error: Could not create artwork directory. return False, util.localize(32010) log.info("Iterating Rom Collections") rccount = 1 # always do full reimports when in rescrape-mode enableFullReimport = isRescrape or __addon__.getSetting(util.SETTING_RCB_ENABLEFULLREIMPORT).upper() == 'TRUE' log.info("enableFullReimport: {0}".format(enableFullReimport)) continueUpdate = True # Added variable to allow user to continue on errors ignoreErrors = False for romCollection in list(romCollections.values()): # timestamp1 = time.clock() # check if import was canceled if not continueUpdate: log.info("Game import canceled") break # prepare Header for ProgressDialog # 32122 = Importing Rom Collection progDialogRCHeader = util.localize(32122) + " (%i / %i): %s" % ( rccount, len(romCollections), romCollection.name) rccount += 1 log.info("current Rom Collection: {0}".format(romCollection.name)) # Read settings for current Rom Collection log.info("ignoreOnScan: {0}".format(romCollection.ignoreOnScan)) if romCollection.ignoreOnScan: log.info("current Rom Collection will be ignored.") # self.scrapeResultsFile.write('Rom Collection will be ignored.\n') continue log.info("update is allowed for current rom collection: {0}".format(romCollection.allowUpdate)) log.info("max folder depth: {0}".format(romCollection.maxFolderDepth)) files = self.getRomFilesByRomCollection(romCollection, enableFullReimport) if len(files) == 0: log.info(u"No files found for rom collection {0}, skipping".format(romCollection.name)) continue log.info(u"Found {0} game files for rom collection {1}".format(len(files), romCollection.name)) # itemCount is used for percentage in ProgressDialogGUI self._gui.itemCount = len(files) + 1 successfulFiles = 0 lastgamename = '' lastGameId = None for fileidx, filename in enumerate(files): try: #Give kodi a chance to interrupt the process #HACK: we should use monitor.abortRequested() or monitor.waitForAbort() #but for some reason only xbmc.abortRequested returns True if monitor.abortRequested(): log.info("Kodi requests abort. Cancel Update.") break log.info("Scraping for %s" % filename) gamenameFromFile = romCollection.getGamenameFromFilename(filename) # check if we are handling one of the additional disks of a multi rom game isMultiRomGame = (gamenameFromFile == lastgamename) lastgamename = gamenameFromFile if isMultiRomGame: # Add this entry as a file under the game ID and move on log.info("Detected %s as a multirom game (previous game was %s)" % (filename, lastgamename)) if lastGameId is None: log.error("Game detected as multi rom game, but lastGameId is None.") continue fileType = FileType() fileType.id, fileType.name, fileType.parent = 0, "rcb_rom", "game" self.insertFile(filename, lastGameId, fileType, None, None, None) self.gdb.commit() del fileType continue log.info("Start scraping info for game: %s" % gamenameFromFile) # 32123 = Importing Game msg = "%s: %s" %(util.localize(32123), gamenameFromFile) continueUpdate = self._gui.writeMsg(msg, fileidx + 1) if not continueUpdate: log.info("Game import canceled by user") break # check if this file already exists in DB continueUpdate, isUpdate, gameId = self.checkRomfileAlreadyExists(filename, enableFullReimport) if not continueUpdate: continue foldername = self.getFoldernameFromRomFilename(filename) results, artScrapers = self.useSingleScrapers(romCollection, filename, gamenameFromFile, progDialogRCHeader, fileidx + 1) if len(results) == 0: # lastgamename = "" results = None # Variables to process Art Download Info self._guiDict.update({'dialogHeaderKey': progDialogRCHeader, 'gameNameKey': gamenameFromFile, 'scraperSiteKey': artScrapers, 'fileCountKey': (fileidx + 1)}) del artScrapers #Give kodi a chance to interrupt the process #HACK: we should use monitor.abortRequested() or monitor.waitForAbort() #but for some reason only xbmc.abortRequested returns True if monitor.abortRequested(): log.info("Kodi requests abort. Cancel Update.") break # Add 'gui' and 'dialogDict' parameters to function lastGameId = self.insertGameFromDesc(results, gamenameFromFile, romCollection, [filename], foldername, isUpdate, gameId) del results, foldername if lastGameId is not None: log.info("Successfully added %s" % gamenameFromFile) successfulFiles += 1 # Check if all first 10 games have errors - Modified to allow user to continue on errors if fileidx > 9 and successfulFiles == 0 and not ignoreErrors: #32124 = Continue #32125 = Continue and Ignore Errors #32126 = Cancel #32127 = First 10 games could not be imported. options = [util.localize(32124), util.localize(32125), util.localize(32126)] answer = xbmcgui.Dialog().select(util.localize(32127), options) if answer == 1: # Continue and ignore errors ignoreErrors = True elif answer == 2: # Cancel #32128 = Import canceled. #32129 = Please check kodi.log for details. message = "%s[CR]%s" % (util.localize(32128), util.localize(32129)) xbmcgui.Dialog().ok(util.SCRIPTNAME, message) continueUpdate = False break except ScraperExceededAPIQuoteException: #32128 = Import canceled. #32043 = API quota for current scraper exceeded. xbmcgui.Dialog().ok(util.localize(32128), util.localize(32043)) # Abort the scraping entirely break except Exception as exc: log.warn(u"An error occurred while adding game %s: %s" % (gamenameFromFile, exc)) self.missingDescFile.add_entry(gamenameFromFile) continue # timestamp2 = time.clock() # diff = (timestamp2 - timestamp1) * 1000 # print "load %i games in %d ms" % (self.getListSize(), diff) self._gui.writeMsg("Done.", self._gui.itemCount) log.info("Update finished") return True, ''
def updateDB(self, gdb, gui, romCollections, isRescrape): self.gdb = gdb self._gui = gui log.info("Start Update DB") log.info("Iterating Rom Collections") rccount = 1 # always do full reimports when in rescrape-mode enableFullReimport = isRescrape or __addon__.getSetting(util.SETTING_RCB_ENABLEFULLREIMPORT).upper() == 'TRUE' log.info("enableFullReimport: {0}".format(enableFullReimport)) continueUpdate = True # Added variable to allow user to continue on errors ignoreErrors = False for romCollection in romCollections.values(): # timestamp1 = time.clock() # check if import was canceled if not continueUpdate: log.info("Game import canceled") break # prepare Header for ProgressDialog progDialogRCHeader = util.localize(32122) + " (%i / %i): %s" % ( rccount, len(romCollections), romCollection.name) rccount += 1 log.info("current Rom Collection: {0}".format(romCollection.name)) # Read settings for current Rom Collection log.info("ignoreOnScan: {0}".format(romCollection.ignoreOnScan)) if romCollection.ignoreOnScan: log.info("current Rom Collection will be ignored.") # self.scrapeResultsFile.write('Rom Collection will be ignored.\n') continue log.info("update is allowed for current rom collection: {0}".format(romCollection.allowUpdate)) log.info("max folder depth: {0}".format(romCollection.maxFolderDepth)) files = self.getRomFilesByRomCollection(romCollection, enableFullReimport) if len(files) == 0: log.info(u"No files found for rom collection {0}, skipping".format(romCollection.name)) continue log.info(u"Found {0} game files for rom collection {1}".format(len(files), romCollection.name)) # itemCount is used for percentage in ProgressDialogGUI self._gui.itemCount = len(files) + 1 successfulFiles = 0 lastgamename = '' lastGameId = None for fileidx, filename in enumerate(files): try: #Give kodi a chance to interrupt the process #HACK: we should use monitor.abortRequested() or monitor.waitForAbort() #but for some reason only xbmc.abortRequested returns True if monitor.abortRequested() or xbmc.abortRequested: log.info("Kodi requests abort. Cancel Update.") break log.info("Scraping for %s" % filename) gamenameFromFile = romCollection.getGamenameFromFilename(filename) # check if we are handling one of the additional disks of a multi rom game isMultiRomGame = (gamenameFromFile == lastgamename) lastgamename = gamenameFromFile if isMultiRomGame: # Add this entry as a file under the game ID and move on log.info("Detected %s as a multirom game (previous game was %s)" % (filename, lastgamename)) if lastGameId is None: log.error("Game detected as multi rom game, but lastGameId is None.") continue fileType = FileType() fileType.id, fileType.name, fileType.parent = 0, "rcb_rom", "game" self.insertFile(filename, lastGameId, fileType, None, None, None) self.gdb.commit() del fileType continue log.info("Start scraping info for game: %s" % gamenameFromFile) continueUpdate = self._gui.writeMsg(progDialogRCHeader, util.localize(32123) + ": " + gamenameFromFile, "", fileidx + 1) if not continueUpdate: log.info("Game import canceled by user") break # check if this file already exists in DB continueUpdate, isUpdate, gameId = self.checkRomfileAlreadyExists(filename, enableFullReimport) if not continueUpdate: continue results = {} foldername = self.getFoldernameFromRomFilename(filename) artScrapers = {} results, artScrapers = self.useSingleScrapers(romCollection, filename, gamenameFromFile, progDialogRCHeader, fileidx + 1) if len(results) == 0: # lastgamename = "" results = None # Variables to process Art Download Info self._guiDict.update({'dialogHeaderKey': progDialogRCHeader, 'gameNameKey': gamenameFromFile, 'scraperSiteKey': artScrapers, 'fileCountKey': (fileidx + 1)}) del artScrapers #Give kodi a chance to interrupt the process #HACK: we should use monitor.abortRequested() or monitor.waitForAbort() #but for some reason only xbmc.abortRequested returns True if monitor.abortRequested() or xbmc.abortRequested: log.info("Kodi requests abort. Cancel Update.") break # Add 'gui' and 'dialogDict' parameters to function lastGameId = self.insertGameFromDesc(results, gamenameFromFile, romCollection, [filename], foldername, isUpdate, gameId) del results, foldername if lastGameId is not None: log.info("Successfully added %s" % gamenameFromFile) successfulFiles += 1 # Check if all first 10 games have errors - Modified to allow user to continue on errors if fileidx > 9 and successfulFiles == 0 and not ignoreErrors: options = [util.localize(32124), util.localize(32125), util.localize(32126)] answer = xbmcgui.Dialog().select(util.localize(32127), options) if answer == 1: # Continue and ignore errors ignoreErrors = True elif answer == 2: # Cancel xbmcgui.Dialog().ok(util.SCRIPTNAME, util.localize(32128), util.localize(32129)) continueUpdate = False break except ScraperExceededAPIQuoteException as ke: xbmcgui.Dialog().ok(util.localize(32128), "The API key for a scraper was exceeded") # Abort the scraping entirely break except Exception as exc: log.warn(u"An error occurred while adding game %s: %s" % (gamenameFromFile, exc)) self.missingDescFile.add_entry(gamenameFromFile) continue # timestamp2 = time.clock() # diff = (timestamp2 - timestamp1) * 1000 # print "load %i games in %d ms" % (self.getListSize(), diff) self._gui.writeMsg("Done.", "", "", self._gui.itemCount) log.info("Update finished") return True, ''
def useSingleScrapers(self, romCollection, romFile, gamenameFromFile, progDialogRCHeader, fileCount): """Scrape site for game metadata Args: romCollection: gamenameFromFile: progDialogRCHeader: fileCount: Returns: dict for the game result: {'SearchKey': ['Chrono Trigger'], 'Publisher': ['Squaresoft'], 'Description': ["The millennium. A portal is opened. The chain of time is broken...], 'Players': ['1'], 'Platform': ['Super Nintendo (SNES)'], 'Game': ['Chrono Trigger'], 'Filetypeboxfront': ['http://thegamesdb.net/banners/boxart/original/front/1255-1.jpg'], 'Filetypeboxback': ['http://thegamesdb.net/banners/boxart/original/back/1255-1.jpg'], 'Filetypescreenshot': ['http://thegamesdb.net/banners/screenshots/1255-1.jpg', 'http://thegamesdb.net/banners/screenshots/1255-2.jpg', 'http://thegamesdb.net/banners/screenshots/1255-3.jpg', 'http://thegamesdb.net/banners/screenshots/1255-4.jpg', 'http://thegamesdb.net/banners/screenshots/1255-5.jpg'], 'Filetypefanart': ['http://thegamesdb.net/banners/fanart/original/1255-1.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-10.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-11.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-2.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-3.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-4.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-5.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-6.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-7.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-8.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-9.jpg'], 'Genre': ['Role-Playing'], 'Developer': ['Squaresoft']} dict for artwork urls: {'Filetypefanart': 'thegamesdb.net', 'Filetypeboxback': 'thegamesdb.net', 'Filetypescreenshot': 'thegamesdb.net', 'Filetypeboxfront': 'thegamesdb.net'} Note - this only contains entries for artwork that was found (i.e. is not empty list) """ gameresult = {} artScrapers = {} scraperSite = romCollection.scraperSites[0] try: #first check if a local nfo file is available nfoscraper = NFO_Scraper() nfofile = nfoscraper.get_nfo_path(gamenameFromFile, romCollection.name, romFile) if xbmcvfs.exists(nfofile) and __addon__.getSetting(util.SETTING_RCB_PREFERLOCALNFO).upper() == 'TRUE': log.info("Found local nfo file. Using this to scrape info.") newscraper = nfoscraper else: newscraper = AbstractScraper().get_scraper_by_name(scraperSite.name) #set path to desc file (only useful for offline scrapers) newscraper.path = scraperSite.path results = newscraper.search(gamenameFromFile, romCollection.name) log.debug(u"Searching for %s - found %s results: %s" % (gamenameFromFile, len(results), results)) except ScraperExceededAPIQuoteException as ke: # API key is invalid - we need to stop scraping log.error("Scraper exceeded API key, stopping scraping") raise except Exception as e: log.error("Error searching for %s using scraper %s - %s %s" % ( gamenameFromFile, scraperSite.name, type(e), e)) return gameresult, artScrapers if results == []: log.warn("No search results found for %s using scraper %s" % (gamenameFromFile, scraperSite.name)) return gameresult, artScrapers matched = Matcher().getBestResults(results, gamenameFromFile) if matched is None: log.error("No matches found for %s, skipping" % gamenameFromFile) return gameresult, artScrapers log.debug("After matching: %s" % matched) try: retrievedresult = newscraper.retrieve(matched['id'], romCollection.name) log.debug(u"Retrieving %s - found %s" % (matched['id'], retrievedresult)) except Exception as e: # FIXME TODO Catch exceptions specifically log.error("Error retrieving %s - %s %s" % (matched['id'], type(e), e)) return gameresult, artScrapers # Update the gameresult with any new fields gameresult = self.addNewElements(gameresult, retrievedresult) self._gui.writeMsg(progDialogRCHeader, util.localize(32123) + ": " + gamenameFromFile, scraperSite.name + " - " + util.localize(32131), fileCount) # Find Filetypes and Scrapers for Art Download # FIXME TODO The following is kept to keep artwork downloading working as it currently is. We already have # the URLs and so could handle/download here, rather than deferring if len(gameresult) > 0: for path in romCollection.mediaPaths: thumbKey = 'Filetype' + path.fileType.name if len(self.resolveParseResult(gameresult, thumbKey)) > 0: if (thumbKey in artScrapers) == 0: artScrapers[thumbKey] = scraperSite.name log.debug(u"After scraping, result = %s, artscrapers = %s" % (gameresult, artScrapers)) return gameresult, artScrapers
def __launchNonXbox(self, cmd, gameRow, precmd, postcmd, roms, gui, listitem): log.info("launchEmu on non-xbox") screenModeToggled = False # use libretro core to play game if self.romCollection.useBuiltinEmulator: log.info("launching game with internal emulator") rom = roms[0] gameclient = self.romCollection.gameclient # HACK: use alternateGameCmd as gameclient if gameRow[util.GAME_alternateGameCmd] is not None and gameRow[ util.GAME_alternateGameCmd] != "": gameclient = str(gameRow[util.GAME_alternateGameCmd]) log.info("Preferred gameclient: " + gameclient) log.info("Setting platform: " + self.romCollection.name) if listitem is None: listitem = xbmcgui.ListItem(rom, "0", "", "") parameters = {"platform": self.romCollection.name} if gameclient != "": parameters["gameclient"] = gameclient listitem.setInfo(type="game", infoLabels=parameters) log.info("launching rom: " + rom) gui.player.play(rom, listitem) # xbmc.executebuiltin('PlayMedia(\"%s\", platform=%s, gameclient=%s)' %(rom, romCollection.name, romCollection.gameclient)) return if not self.romCollection.useEmuSolo: screenMode = xbmc.getInfoLabel("System.Screenmode") log.info("screenMode: " + screenMode) isFullScreen = screenMode.endswith("Full Screen") toggleScreenMode = __addon__.getSetting( util.SETTING_RCB_TOGGLESCREENMODE).upper() == 'TRUE' if isFullScreen and toggleScreenMode: log.info("Toggling to windowed mode") # this minimizes xbmc some apps seems to need it xbmc.executeJSONRPC(KODI_JSONRPC_TOGGLE_FULLSCREEN) screenModeToggled = True log.info("launch emu") self.__executePreCommand(precmd) self.__preDelay() self.__audioSuspend() self.__executeCommand(cmd) log.info("launch emu done") self.__postDelay() self.__audioResume() self.__executePostCommand(postcmd) if screenModeToggled: log.info("Toggle to Full Screen mode") # this brings xbmc back xbmc.executeJSONRPC(KODI_JSONRPC_TOGGLE_FULLSCREEN)
def __audioSuspend(self): if __addon__.getSetting(util.SETTING_RCB_SUSPENDAUDIO).upper() == 'TRUE': log.debug("Suspending audio") xbmc.executebuiltin("PlayerControl(Stop)") xbmc.enableNavSounds(False) xbmc.audioSuspend()
def __preDelay(self): preDelay = __addon__.getSetting(SETTING_RCB_PRELAUNCHDELAY) if preDelay != '': log.debug("Pre delaying by {0}ms".format(preDelay)) xbmc.sleep(int(float(preDelay)))
def __postDelay(self): postDelay = __addon__.getSetting(SETTING_RCB_POSTLAUNCHDELAY) if postDelay != '': log.debug("Post delaying by {0}ms".format(postDelay)) xbmc.sleep(int(float(postDelay)))
def __audioResume(self): if __addon__.getSetting(util.SETTING_RCB_SUSPENDAUDIO).upper() == 'TRUE': log.debug("Resuming audio") xbmc.audioResume() xbmc.enableNavSounds(True)
def __audioResume(self): if __addon__.getSetting( util.SETTING_RCB_SUSPENDAUDIO).upper() == 'TRUE': log.debug("Resuming audio") xbmc.audioResume() xbmc.enableNavSounds(True)
def launchEmu(self, gdb, gui, gameId, config, listitem): log.info("Begin launcher.launchEmu") gameRow = Game(gdb).getObjectById(gameId) if gameRow is None: log.error("Game with id %s could not be found in database" % gameId) return try: self.romCollection = config.romCollections[str( gameRow[util.GAME_romCollectionId])] except KeyError: log.error("Cannot get rom collection with id: " + str(gameRow[util.GAME_romCollectionId])) gui.writeMsg(util.localize(32034)) return gui.writeMsg(util.localize(32163) + " " + gameRow[util.ROW_NAME]) # Remember viewstate gui.saveViewState(False) cmd = "" precmd = "" postcmd = "" filenameRows = File(gdb).getRomsByGameId(gameRow[util.ROW_ID]) log.info("files for current game: " + str(filenameRows)) cmd, precmd, postcmd, roms = self.__buildCmd(gui, filenameRows, gameRow, False) if not self.romCollection.useBuiltinEmulator: if cmd == '': log.info("No cmd created. Game will not be launched.") return if precmd.strip() == '' or precmd.strip() == 'call': log.info("No precmd created.") if postcmd.strip() == '' or postcmd.strip() == 'call': log.info("No postcmd created.") # solo mode if self.romCollection.useEmuSolo: self.__copyLauncherScriptsToUserdata() # communicate with service via settings __addon__.setSetting(util.SETTING_RCB_LAUNCHONSTARTUP, 'true') # invoke script file that kills xbmc before launching the emulator basePath = os.path.join(util.getAddonDataPath(), 'scriptfiles') if self.env == "win32": if __addon__.getSetting(util.SETTING_RCB_USEVBINSOLOMODE ).lower() == 'true': # There is a problem with quotes passed as argument to windows command shell. This only works with "call" # use vb script to restart xbmc cmd = 'call \"' + os.path.join( basePath, 'applaunch-vbs.bat') + '\" ' + cmd else: # There is a problem with quotes passed as argument to windows command shell. This only works with "call" cmd = 'call \"' + os.path.join( basePath, 'applaunch.bat') + '\" ' + cmd else: cmd = os.path.join(basePath, 'applaunch.sh ') + cmd else: # use call to support paths with whitespaces if self.env == "win32": cmd = 'call ' + cmd # update LaunchCount launchCount = gameRow[util.GAME_launchCount] Game(gdb).update(('launchCount', ), (launchCount + 1, ), gameRow[util.ROW_ID], True) gdb.commit() log.info("cmd: " + cmd) log.info("precmd: " + precmd) log.info("postcmd: " + postcmd) try: self.__launchNonXbox(cmd, gameRow, precmd, postcmd, roms, gui, listitem) gui.writeMsg("") except Exception, (exc): log.error("Error while launching emu: " + str(exc)) gui.writeMsg(util.localize(32035) + ": " + str(exc))
def __launchNonXbox(self, cmd, gameRow, precmd, postcmd, roms, gui, listitem): log.info("launchEmu on non-xbox") screenModeToggled = False # use libretro core to play game if self.romCollection.useBuiltinEmulator: log.info("launching game with internal emulator") rom = roms[0] gameclient = self.romCollection.gameclient # HACK: use alternateGameCmd as gameclient if gameRow[util.GAME_alternateGameCmd] is not None and gameRow[util.GAME_alternateGameCmd] != "": gameclient = str(gameRow[util.GAME_alternateGameCmd]) log.info("Preferred gameclient: " + gameclient) log.info("Setting platform: " + self.romCollection.name) if listitem is None: listitem = xbmcgui.ListItem(rom, "0", "", "") parameters = {"platform": self.romCollection.name} if gameclient != "": parameters["gameclient"] = gameclient listitem.setInfo(type="game", infoLabels=parameters) log.info("launching rom: " + rom) gui.player.play(rom, listitem) # xbmc.executebuiltin('PlayMedia(\"%s\", platform=%s, gameclient=%s)' %(rom, romCollection.name, romCollection.gameclient)) return if not self.romCollection.useEmuSolo: screenMode = xbmc.getInfoLabel("System.Screenmode") log.info("screenMode: " + screenMode) isFullScreen = screenMode.endswith("Full Screen") toggleScreenMode = __addon__.getSetting(util.SETTING_RCB_TOGGLESCREENMODE).upper() == 'TRUE' if isFullScreen and toggleScreenMode: log.info("Toggling to windowed mode") # this minimizes xbmc some apps seems to need it xbmc.executeJSONRPC(KODI_JSONRPC_TOGGLE_FULLSCREEN) screenModeToggled = True log.info("launch emu") self.__executePreCommand(precmd) self.__preDelay() self.__audioSuspend() self.__executeCommand(cmd) log.info("launch emu done") self.__postDelay() self.__audioResume() self.__executePostCommand(postcmd) if screenModeToggled: log.info("Toggle to Full Screen mode") # this brings xbmc back xbmc.executeJSONRPC(KODI_JSONRPC_TOGGLE_FULLSCREEN)
def useSingleScrapers(self, romCollection, romFile, gamenameFromFile, progDialogRCHeader, fileCount): """Scrape site for game metadata Args: romCollection: romFile: gamenameFromFile: progDialogRCHeader: fileCount: Returns: dict for the game result: {'SearchKey': ['Chrono Trigger'], 'Publisher': ['Squaresoft'], 'Description': ["The millennium. A portal is opened. The chain of time is broken...], 'Players': ['1'], 'Platform': ['Super Nintendo (SNES)'], 'Game': ['Chrono Trigger'], 'Filetypeboxfront': ['http://thegamesdb.net/banners/boxart/original/front/1255-1.jpg'], 'Filetypeboxback': ['http://thegamesdb.net/banners/boxart/original/back/1255-1.jpg'], 'Filetypescreenshot': ['http://thegamesdb.net/banners/screenshots/1255-1.jpg', 'http://thegamesdb.net/banners/screenshots/1255-2.jpg', 'http://thegamesdb.net/banners/screenshots/1255-3.jpg', 'http://thegamesdb.net/banners/screenshots/1255-4.jpg', 'http://thegamesdb.net/banners/screenshots/1255-5.jpg'], 'Filetypefanart': ['http://thegamesdb.net/banners/fanart/original/1255-1.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-10.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-11.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-2.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-3.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-4.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-5.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-6.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-7.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-8.jpg', 'http://thegamesdb.net/banners/fanart/original/1255-9.jpg'], 'Genre': ['Role-Playing'], 'Developer': ['Squaresoft']} dict for artwork urls: {'Filetypefanart': 'thegamesdb.net', 'Filetypeboxback': 'thegamesdb.net', 'Filetypescreenshot': 'thegamesdb.net', 'Filetypeboxfront': 'thegamesdb.net'} Note - this only contains entries for artwork that was found (i.e. is not empty list) """ gameresult = {} artScrapers = {} scraperSite = None #search for default scraper if there are more than one for site in romCollection.scraperSites: if site.default: scraperSite = site break #if no default site was found, just use the first one if not scraperSite: if len(romCollection.scraperSites) >= 1: scraperSite = romCollection.scraperSites[0] try: #first check if a local nfo file is available nfoscraper = NFO_Scraper() nfofile = nfoscraper.get_nfo_path(gamenameFromFile, romCollection.name, romFile) if xbmcvfs.exists(nfofile) and __addon__.getSetting(util.SETTING_RCB_PREFERLOCALNFO).upper() == 'TRUE': log.info("Found local nfo file. Using this to scrape info.") newscraper = nfoscraper else: newscraper = AbstractScraper().get_scraper_by_name(scraperSite.name) #set path to desc file (only useful for offline scrapers) newscraper.path = scraperSite.path log.info("Using scraper: %s" % newscraper.name) # 32123 = Importing Game # 32131 = downloading info msg = "%s: %s[CR]%s: %s" %(util.localize(32123), gamenameFromFile, newscraper.name, util.localize(32131)) self._gui.writeMsg(msg, fileCount) results = newscraper.search(gamenameFromFile, romCollection.name) log.debug(u"Searching for %s - found %s results: %s" % (gamenameFromFile, len(results), results)) except ScraperExceededAPIQuoteException as ke: # API key is invalid - we need to stop scraping log.error("Scraper exceeded API key, stopping scraping") raise except Exception as e: log.error("Error searching for %s using scraper %s - %s %s" % ( gamenameFromFile, scraperSite.name, type(e), e)) return gameresult, artScrapers if results == []: log.warn("No search results found for %s using scraper %s" % (gamenameFromFile, scraperSite.name)) return gameresult, artScrapers matched = Matcher().getBestResults(results, gamenameFromFile) if matched is None: log.error("No matches found for %s, skipping" % gamenameFromFile) return gameresult, artScrapers log.debug("After matching: %s" % matched) try: retrievedresult = newscraper.retrieve(matched['id'], romCollection.name) log.debug(u"Retrieving %s - found %s" % (matched['id'], retrievedresult)) except Exception as e: # FIXME TODO Catch exceptions specifically log.error("Error retrieving %s - %s %s" % (matched['id'], type(e), e)) return gameresult, artScrapers # Update the gameresult with any new fields gameresult = self.addNewElements(gameresult, retrievedresult) # Find Filetypes and Scrapers for Art Download # FIXME TODO The following is kept to keep artwork downloading working as it currently is. We already have # the URLs and so could handle/download here, rather than deferring if len(gameresult) > 0: for path in romCollection.mediaPaths: thumbKey = 'Filetype' + path.fileType.name if len(self.resolveParseResult(gameresult, thumbKey)) > 0: if (thumbKey in artScrapers) == 0: artScrapers[thumbKey] = scraperSite.name log.debug(u"After scraping, result = %s, artscrapers = %s" % (gameresult, artScrapers)) return gameresult, artScrapers