def CheckPasswordReuse(skipUserInput): """ Check with user for password reuse. Parameters ---------- skipUserInput : boolean Set to skip user input. Returns ---------- int Integer from -1 to 2 depending on user response. """ goodlogging.Log.Info("EXTRACT", "RAR files needs password to extract") if skipUserInput is False: prompt = "Enter 't' to reuse the last password for just this file, " \ "'a' to reuse for all subsequent files, " \ "'n' to enter a new password for this file " \ "or 's' to enter a new password for all files: " response = goodlogging.Log.Input("EXTRACT", prompt) response = util.ValidUserResponse(response, ('t','a','n','s')) else: response = 'a' if response.lower() == 's': return -1 if response.lower() == 'n': return 0 elif response.lower() == 't': return 1 elif response.lower() == 'a': return 2
def CheckPasswordReuse(skipUserInput): goodlogging.Log.Info("EXTRACT", "RAR files needs password to extract") if skipUserInput is False: prompt = "Enter 't' to reuse the last password for just this file, " \ "'a' to reuse for all subsequent files, " \ "'n' to enter a new password for this file " \ "or 's' to enter a new password for all files: " response = goodlogging.Log.Input("EXTRACT", prompt) response = util.ValidUserResponse(response, ('t', 'a', 'n', 's')) else: response = 'a' if response.lower() == 's': return -1 if response.lower() == 'n': return 0 elif response.lower() == 't': return 1 elif response.lower() == 'a': return 2
def Run(self): """ Renames all TV files from the constructor given file list. It follows a number of key steps: 1) Extract a list of unique show titles from file name and lookup actual show names from database or TV guide. 2) Update each file with showID and showName. 3) Get episode name for all remaining files in valid list. 4) Print file details and generate new file paths. 5) Rename files. 6) List skipped and incompatible files. """ # ------------------------------------------------------------------------ # Get list of unique fileInfo show names and find matching actual show # names from database or TV guide # ------------------------------------------------------------------------ showNameMatchDict = {} uniqueFileShowList = self._GetUniqueFileShowNames(self._fileList) if len(uniqueFileShowList) > 0: goodlogging.Log.Seperator() for fileShowName in uniqueFileShowList: showNameMatchDict[fileShowName] = self._GetShowInfo(fileShowName) goodlogging.Log.NewLine() # ------------------------------------------------------------------------ # Update each file with showID and showName # ------------------------------------------------------------------------ incompatibleFileList = [] validShowFileList = [] for tvFile in self._fileList: if showNameMatchDict[tvFile.fileInfo.showName] is None: incompatibleFileList.append(tvFile) else: tvFile.showInfo.showID = showNameMatchDict[ tvFile.fileInfo.showName].showID tvFile.showInfo.showName = showNameMatchDict[ tvFile.fileInfo.showName].showName validShowFileList.append(tvFile) # ------------------------------------------------------------------------ # Get episode name for all remaining files in valid list # ------------------------------------------------------------------------ if len(validShowFileList) > 0: goodlogging.Log.Seperator() validEpisodeNameFileList = [] goodlogging.Log.Info("RENAMER", "Looking up episode names:\n") for tvFile in validShowFileList: tvFile.showInfo.episodeName = self._guide.EpisodeNameLookUp( tvFile.showInfo.showName, tvFile.showInfo.seasonNum, tvFile.showInfo.episodeNum) if tvFile.showInfo.episodeName is None: incompatibleFileList.append(tvFile) else: validEpisodeNameFileList.append(tvFile) goodlogging.Log.Info( "RENAMER", "{0} S{1}E{2}: {3}".format(tvFile.showInfo.showName, tvFile.showInfo.seasonNum, tvFile.showInfo.episodeNum, tvFile.showInfo.episodeName)) goodlogging.Log.NewLine() # ------------------------------------------------------------------------ # Print file details and generate new file paths # ------------------------------------------------------------------------ goodlogging.Log.Seperator() renameFileList = [] skippedFileList = [] goodlogging.Log.Info("RENAMER", "Generating library paths:\n") if len(validEpisodeNameFileList) == 0: goodlogging.Log.Info("RENAMER", "No compatible files were detected") else: for tvFile in validEpisodeNameFileList: tvFile.Print() goodlogging.Log.NewLine() if self._inPlaceRename is False: tvFile = self._GenerateLibraryPath(tvFile, self._tvDir) else: tvFile.GenerateNewFilePath() if tvFile.fileInfo.newPath is None: incompatibleFileList.append(tvFile) elif tvFile.fileInfo.origPath != tvFile.fileInfo.newPath: renameFileList.append(tvFile) else: skippedFileList.append(tvFile) goodlogging.Log.NewLine() # ------------------------------------------------------------------------ # Rename files # ------------------------------------------------------------------------ goodlogging.Log.Seperator() goodlogging.Log.Info("RENAMER", "Renamable files:\n") if len(renameFileList) == 0: goodlogging.Log.Info("RENAMER", "No renamable files were detected") else: showName = None renameFileList.sort() for tvFile in renameFileList: if showName is None or showName != tvFile.showInfo.showName: showName = tvFile.showInfo.showName goodlogging.Log.Info("RENAMER", "{0}".format(showName)) goodlogging.Log.IncreaseIndent() goodlogging.Log.Info( "RENAMER", "FROM: {0}".format(tvFile.fileInfo.origPath)) goodlogging.Log.Info( "RENAMER", "TO: {0}".format(tvFile.fileInfo.newPath)) goodlogging.Log.DecreaseIndent() goodlogging.Log.NewLine() if self._skipUserInput is False: response = goodlogging.Log.Input( 'RENAMER', "***WARNING*** CONTINUE WITH RENAME PROCESS? [y/n]: ") response = util.ValidUserResponse(response, ('y', 'n')) else: response = 'y' if response == 'n': goodlogging.Log.Info("RENAMER", "Renaming process skipped") elif response == 'y': goodlogging.Log.NewLine() if self._inPlaceRename is False: goodlogging.Log.Info("RENAMER", "Adding files to TV library:\n") else: goodlogging.Log.Info("RENAMER", "Renaming files:\n") for tvFile in renameFileList: self._MoveFileToLibrary(tvFile.fileInfo.origPath, tvFile.fileInfo.newPath) goodlogging.Log.NewLine() # ------------------------------------------------------------------------ # List skipped files # ------------------------------------------------------------------------ if len(skippedFileList) > 0: goodlogging.Log.Seperator() goodlogging.Log.Info("RENAMER", "Skipped files:") goodlogging.Log.IncreaseIndent() for tvFile in skippedFileList: if tvFile.fileInfo.origPath == tvFile.fileInfo.newPath: goodlogging.Log.Info( "RENAMER", "{0} (No rename required)".format( tvFile.fileInfo.origPath)) else: goodlogging.Log.Info( "RENAMER", "{0} (Unknown reason)".format( tvFile.fileInfo.origPath)) goodlogging.Log.DecreaseIndent() # ------------------------------------------------------------------------ # List incompatible files # ------------------------------------------------------------------------ if len(incompatibleFileList) > 0: goodlogging.Log.Seperator() goodlogging.Log.Info("RENAMER", "Incompatible files:") goodlogging.Log.IncreaseIndent() for tvFile in incompatibleFileList: if tvFile.showInfo.showName is None: goodlogging.Log.Info( "RENAMER", "{0} (Missing show name)".format( tvFile.fileInfo.origPath)) elif tvFile.showInfo.episodeName is None: goodlogging.Log.Info( "RENAMER", "{0} (Missing episode name)".format( tvFile.fileInfo.origPath)) elif tvFile.fileInfo.newPath is None: goodlogging.Log.Info( "RENAMER", "{0} (Failed to create new file path)".format( tvFile.fileInfo.origPath)) else: goodlogging.Log.Info( "RENAMER", "{0} (Unknown reason)".format( tvFile.fileInfo.origPath)) goodlogging.Log.DecreaseIndent()
def _GetShowID(self, stringSearch, origStringSearch=None): """ Search for given string as an existing entry in the database file name table or, if no match is found, as a show name from the TV guide. If an exact match is not found in the database the user can accept or decline the best match from the TV guide or can provide an alternate match to lookup. Parameters ---------- stringSearch : string String to look up in database or guide. origStringSearch : string [optional: default = None] Original search string, used by recusive function calls. Returns ---------- tvfile.ShowInfo or None If no show id could be found this returns None, otherwise it returns a tvfile.ShowInfo object containing show name and show id. """ showInfo = tvfile.ShowInfo() if origStringSearch is None: goodlogging.Log.Info( "RENAMER", "Looking up show ID for: {0}".format(stringSearch)) origStringSearch = stringSearch goodlogging.Log.IncreaseIndent() showInfo.showID = self._db.SearchFileNameTable(stringSearch) if showInfo.showID is None: goodlogging.Log.Info( "RENAMER", "No show ID match found for '{0}' in database".format( stringSearch)) showNameList = self._guide.ShowNameLookUp(stringSearch) if self._skipUserInput is True: if len(showNameList) == 1: showName = showNameList[0] goodlogging.Log.Info( "RENAMER", "Automatic selection of showname: {0}".format( showName)) else: showName = None goodlogging.Log.Info( "RENAMER", "Show skipped - could not make automatic selection of showname" ) else: showName = util.UserAcceptance(showNameList) if showName in showNameList: libEntry = self._db.SearchTVLibrary(showName=showName) if libEntry is None: if self._skipUserInput is True: response = 'y' else: goodlogging.Log.Info( "RENAMER", "No show by this name found in TV library database. Is this a new show for the database?" ) response = goodlogging.Log.Input( "RENAMER", "Enter 'y' (yes), 'n' (no) or 'ls' (list existing shows): " ) response = util.ValidUserResponse( response, ('y', 'n', 'ls')) if response.lower() == 'ls': dbLibList = self._db.SearchTVLibrary() if dbLibList is None: goodlogging.Log.Info("RENAMER", "TV library is empty") response = 'y' else: dbShowNameList = [i[1] for i in dbLibList] dbShowNameStr = ', '.join(dbShowNameList) goodlogging.Log.Info( "RENAMER", "Existing shows in database are: {0}". format(dbShowNameStr)) response = goodlogging.Log.Input( "RENAMER", "Is this a new show? [y/n]: ") response = util.ValidUserResponse( response, ('y', 'n')) if response.lower() == 'y': showInfo.showID = self._db.AddShowToTVLibrary(showName) showInfo.showName = showName else: try: dbShowNameList except NameError: dbLibList = self._db.SearchTVLibrary() if dbLibList is None: goodlogging.Log.Info( "RENAMER", "No show ID found - TV library is empty") return None dbShowNameList = [i[1] for i in dbLibList] while showInfo.showID is None: matchShowList = util.GetBestMatch( showName, dbShowNameList) showName = util.UserAcceptance(matchShowList) if showName is None: goodlogging.Log.Info( "RENAMER", "No show ID found - could not match to existing show" ) return None elif showName in matchShowList: showInfo.showID = self._db.SearchTVLibrary( showName=showName)[0][0] showInfo.showName = showName else: showInfo.showID = libEntry[0][0] self._db.AddToFileNameTable(origStringSearch, showInfo.showID) goodlogging.Log.DecreaseIndent() return showInfo elif showName is None: goodlogging.Log.DecreaseIndent() return None else: goodlogging.Log.DecreaseIndent() return self._GetShowID(showName, origStringSearch) else: goodlogging.Log.Info( "RENAMER", "Match found: show ID = {0}".format(showInfo.showID)) if origStringSearch != stringSearch: self._db.AddToFileNameTable(origStringSearch, showInfo.showID) goodlogging.Log.DecreaseIndent() return showInfo
def ManualUpdateTables(self): """ Allow user to manually update the database tables. User options from initial prompt are: - 'ls' : print database contents - 'a' : add an row to a database table - 'd' : delete a single table row - 'p' : delete an entire table (purge) - 'f' : finish updates and continue - 'x' : finish updates and exit Selecting add, delete or purge will proceed to a further prompt where the user can enter exactly what information should be added or deleted. """ goodlogging.Log.Info("DB", "Starting manual database update:\n") updateFinished = False # Loop until the user continues program flow or exits while not updateFinished: prompt = "Enter 'ls' to print the database contents, " \ "'a' to add a table entry, " \ "'d' to delete a single table row, " \ "'p' to select a entire table to purge, " \ "'f' to finish or " \ "'x' to exit: " response = goodlogging.Log.Input("DM", prompt) goodlogging.Log.NewLine() goodlogging.Log.IncreaseIndent() # Exit program if response.lower() == 'x': goodlogging.Log.Fatal("DB", "Program exited by user response") # Finish updating database elif response.lower() == 'f': updateFinished = True # Print database tables elif response.lower() == 'ls': self.PrintAllTables() # Purge a given table elif response.lower() == 'p': response = goodlogging.Log.Input("DM", "Enter database table to purge or 'c' to cancel: ") # Go back to main update selection if response.lower() == 'c': goodlogging.Log.Info("DB", "Database table purge cancelled") # Purge table else: if response in self._tableDict.keys(): self._PrintDatabaseTable(response) deleteConfirmation = goodlogging.Log.Input("DB", "***WARNING*** DELETE ALL ROWS FROM {0} TABLE? [y/n]: ".format(response)) deleteConfirmation = util.ValidUserResponse(deleteConfirmation, ('y', 'n')) if deleteConfirmation.lower() == 'n': goodlogging.Log.Info("DB", "Database table purge cancelled") else: self._PurgeTable(response) goodlogging.Log.Info("DB", "{0} database table purged".format(response)) else: goodlogging.Log.Info("DB", "Unknown table name ({0}) given to purge".format(response)) # Add new row to table elif response.lower() == 'a': addFinished = False while not addFinished: prompt = "Enter new database row (in format TABLE COL1=VAL COL2=VAL etc) " \ "or 'c' to cancel: " response = goodlogging.Log.Input("DM", prompt) # Go back to main update selection if response.lower() == 'c': goodlogging.Log.Info("DB", "Database table add cancelled") addFinished = True # Add row to table else: self._UpdateDatabaseFromResponse(response, 'ADD') # Delete row(s) from table elif response.lower() == 'd': deleteFinished = False while not deleteFinished: prompt = "Enter database row to delete (in format TABLE COL1=VAL COL2=VAL etc) " \ "or 'c' to cancel: " response = goodlogging.Log.Input("DM", prompt) # Go back to main update selection if response.lower() == 'c': goodlogging.Log.Info("DB", "Database table row delete cancelled") deleteFinished = True # Delete row(s) from table else: self._UpdateDatabaseFromResponse(response, 'DEL') # Unknown user input given else: goodlogging.Log.Info("DB", "Unknown response") goodlogging.Log.DecreaseIndent() goodlogging.Log.NewLine() goodlogging.Log.Info("DB", "Manual database update complete.") self.PrintAllTables()
def _UpdateDatabaseFromResponse(self, response, mode): """ Update database table given a user input in the form "TABLENAME COL1=VAL1 COL2=VAL2". Either ADD or DELETE from table depending on mode argument. If the change succeeds the updated table is printed to stdout. Parameters ---------- response : string User input. mode : string Valid values are 'ADD' or 'DEL'. Returns ---------- None Will always return None. There are numerous early returns in the cases where the database update cannot proceed for any reason. """ # Get tableName from user input (form TABLENAME COL1=VAL1 COL2=VAL2 etc) try: tableName, tableColumns = response.split(' ', 1) except ValueError: goodlogging.Log.Info("DB", "Database update failed - failed to extract table name from response") return None # Check user input against known table list if tableName not in self._tableDict.keys(): goodlogging.Log.Info("DB", "Database update failed - unkown table name: {0}".format(tableName)) return None # Build re pattern to extract column from user input (form TABLENAME COL1=VAL1 COL2=VAL2 etc) rowSelect = [] for column in self._tableDict[tableName]: colPatternList = ['(?:{0})'.format(i) for i in self._tableDict[tableName] if i != column] colPatternList.append('(?:$)') colPatternMatch = '|'.join(colPatternList) matchPattern = '{0}.*?{1}=(.+?)\s*(?:{2})'.format(tableName, column, colPatternMatch) match = re.findall(matchPattern, response) # Match should be in form [(VAL1, VAL2, VAL3, etc.)] if len(match) == 1: rowSelect.append((column, match[0])) elif len(match) > 1: goodlogging.Log.Info('DB', 'Database update failed - multiple matches found for table {0} column {1}'.format(tableName, column)) return None if len(rowSelect) == 0: goodlogging.Log.Info('DB', 'Database update failed - no row selection critera found in response') return None # Print selected rows rowCount = self._PrintDatabaseTable(tableName, rowSelect) # Do DELETE flow if mode.upper() == 'DEL': if rowCount == 0: goodlogging.Log.Info("DB", "Database update failed - no rows found for given search critera: {0}".format(response)) return None deleteConfirmation = goodlogging.Log.Input("DB", "***WARNING*** DELETE THESE ROWS FROM {0} TABLE? [y/n]: ".format(tableName)) deleteConfirmation = util.ValidUserResponse(deleteConfirmation, ('y', 'n')) if deleteConfirmation.lower() == 'n': goodlogging.Log.Info("DB", "Database table row delete cancelled") return None # Build delete database query (form DELETE FROM TableName WHERE COL1=?, COL2=?) dbQuery = "DELETE FROM {0}".format(tableName) \ + " WHERE " \ + ' AND '.join(['{0}=?'.format(i) for i, j in rowSelect]) dbQueryParams = [j for i, j in rowSelect] self._ActionDatabase(dbQuery, dbQueryParams) goodlogging.Log.Info("DB", "Deleted {0} row(s) from database table {0}:".format(rowCount, tableName)) # Do ADD flow elif mode.upper() == 'ADD': if rowCount != 0: goodlogging.Log.Info("DB", "Database update failed - a row already exists for the given critera: {0}".format(response)) return None # Build insert database query (form INSERT INTO TableName (COL1, COL2) VALUES (?,?)) dbQuery = "INSERT INTO {0} (".format(tableName) \ + ', '.join(['{0}'.format(i) for i, j in rowSelect]) \ + ") VALUES (" \ + ', '.join(['?']*len(rowSelect)) \ + ")" dbQueryParams = [j for i, j in rowSelect] self._ActionDatabase(dbQuery, dbQueryParams) goodlogging.Log.Info("DB", "Added row to database table {0}:".format(tableName)) # Print resulting database table self._PrintDatabaseTable(tableName)
def _GetShowID(self, stringSearch, origStringSearch=None): showInfo = tvfile.ShowInfo() if origStringSearch is None: goodlogging.Log.Info( "RENAMER", "Looking up show ID for: {0}".format(stringSearch)) origStringSearch = stringSearch goodlogging.Log.IncreaseIndent() showInfo.showID = self._db.SearchFileNameTable(stringSearch) if showInfo.showID is None: goodlogging.Log.Info( "RENAMER", "No show ID match found for '{0}' in database".format( stringSearch)) showNameList = self._guide.ShowNameLookUp(stringSearch) if self._skipUserInput is True: if len(showNameList) == 1: showName = showNameList[0] goodlogging.Log.Info( "RENAMER", "Automatic selection of showname: {0}".format( showName)) else: showName = None goodlogging.Log.Info( "RENAMER", "Show skipped - could not make automatic selection of showname" ) else: showName = util.UserAcceptance(showNameList) if showName in showNameList: libEntry = self._db.SearchTVLibrary(showName=showName) if libEntry is None: if self._skipUserInput is True: response = 'y' else: goodlogging.Log.Info( "RENAMER", "No show by this name found in TV library database. Is this a new show for the database?" ) response = goodlogging.Log.Input( "RENAMER", "Enter 'y' (yes), 'n' (no) or 'ls' (list existing shows): " ) response = util.ValidUserResponse( response, ('y', 'n', 'ls')) if response.lower() == 'ls': dbLibList = self._db.SearchTVLibrary() if dbLibList is None: goodlogging.Log.Info("RENAMER", "TV library is empty") response = 'y' else: dbShowNameList = [i[1] for i in dbLibList] dbShowNameStr = ', '.join(dbShowNameList) goodlogging.Log.Info( "RENAMER", "Existing shows in database are: {0}". format(dbShowNameStr)) response = goodlogging.Log.Input( "RENAMER", "Is this a new show? [y/n]: ") response = util.ValidUserResponse( response, ('y', 'n')) if response.lower() == 'y': showInfo.showID = self._db.AddShowToTVLibrary(showName) showInfo.showName = showName else: try: dbShowNameList except NameError: dbLibList = self._db.SearchTVLibrary() if dbLibList is None: goodlogging.Log.Info( "RENAMER", "No show ID found - TV library is empty") return None dbShowNameList = [i[1] for i in dbLibList] finally: while showInfo.showID is None: matchShowList = util.GetBestMatch( showName, dbShowNameList) showName = util.UserAcceptance(matchShowList) if showName is None: goodlogging.Log.Info( "RENAMER", "No show ID found - could not match to existing show" ) return None elif showName in matchShowList: showInfo.showID = self._db.SearchTVLibrary( showName=showName)[0][0] showInfo.showName = showName else: showInfo.showID = libEntry[0][0] self._db.AddToFileNameTable(origStringSearch, showInfo.showID) goodlogging.Log.DecreaseIndent() return showInfo elif showName is None: goodlogging.Log.DecreaseIndent() return None else: goodlogging.Log.DecreaseIndent() return self._GetShowID(showName, origStringSearch) else: goodlogging.Log.Info( "RENAMER", "Match found: show ID = {0}".format(showInfo.showID)) if origStringSearch != stringSearch: self._db.AddToFileNameTable(origStringSearch, showInfo.showID) goodlogging.Log.DecreaseIndent() return showInfo