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.

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()