def copy_file(old, new): """Copy 'old' file to 'new'. """ p("Copying %s to %s" % (old, new)) log().debug("Copying %r to %r" % (old, new)) shutil.copyfile(old, new) shutil.copystat(old, new)
def tvnamer(paths): """Main tvnamer function, takes an array of paths, does stuff. """ # Warn about move_files function if Config['move_files_enable']: import warnings warnings.warn("The move_files feature is still under development. " "Be very careful with it.\n" "It has not been heavily tested, and is not recommended for " "general use yet.") if Config['force_name'] is not None: import warnings warnings.warn("The --name argument is a temporary solution, and will" "be removed at some point in the future. Do no depend on it") p("#" * 20) p("# Starting tvnamer") episodes_found = [] for cfile in findFiles(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename, e: warn("Invalid filename: %s" % e) else: if episode.seriesname is None: warn("Parsed filename did not contain series name, skipping: %s" % cfile) else: episodes_found.append(episode)
def rename_file(old, new): """Rename 'old' file to 'new'. Both files must be on the same partition. Preserves access and modification time. """ p("Renaming %s to %s" % (old, new)) log().debug("Renaming %r to %r" % (old, new)) stat = os.stat(old) os.rename(old, new) os.utime(new, (stat.st_atime, stat.st_mtime))
def rename_file(old, new): p("rename %s to %s" % (old, new)) stat = os.stat(old) os.rename(old, new) try: os.utime(new, (stat.st_atime, stat.st_mtime)) except OSError, ex: if ex.errno == errno.EPERM: warn("WARNING: Could not preserve times for %s " "(owner UID mismatch?)" % new) else: raise
def doMoveFile(cnamer, destDir): """Moves file to destDir""" if not Config['move_files_enable']: raise ValueError("move_files feature is disabled but doMoveFile was called") if Config['move_files_destination'] is None: raise ValueError("Config value for move_files_destination cannot be None if move_files_enabled is True") p("New directory:", destDir) try: cnamer.newPath(destDir) except OSError, e: warn(unicode(e))
def newPath(self, new_path = None, new_fullpath = None, force = False, always_copy = False, always_move = False, create_dirs = True, getPathPreview = False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied. If the target file already exists, it will raise OSError unless force is True. """ old_fileinfo = self.filename p("self.filename: %s" % (self.filename)) p("old_fileinfo: %s" % (old_fileinfo)) if always_copy and always_move: raise ValueError("Both always_copy and always_move cannot be specified") if (new_path is None and new_fullpath is None) or (new_path is not None and new_fullpath is not None): raise ValueError("Specify only new_dir or new_fullpath") if new_path is not None: old_dir, old_filename = os.path.split(self.filename) # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) else: old_dir, old_filename = os.path.split(self.filename) # Join new filepath to old one (to handle realtive dirs) new_fullpath = os.path.abspath(os.path.join(old_dir, new_fullpath)) new_dir = os.path.dirname(new_fullpath) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: p("Creating directory %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise
def processFile(tvdb_instance, episode): """Gets episode name, prompts user for input """ p("#" * 20) p("# Processing file: %s" % episode.fullfilename) if len(Config['input_filename_replacements']) > 0: replaced = applyCustomInputReplacements(episode.fullfilename) p("# With custom replacements: %s" % (replaced)) # Use force_name option. Done after input_filename_replacements so # it can be used to skip the replacements easily if Config['force_name'] is not None: episode.seriesname = Config['force_name'] p("# Detected series: %s (%s)" % (episode.seriesname, episode.number_string())) try: episode.populateFromTvdb(tvdb_instance, force_name=Config['force_name'], series_id=Config['series_id']) except (DataRetrievalError, ShowNotFound), errormsg: if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return else: warn(errormsg)
def newName(self, newName, force = False): """Renames a file, keeping the path the same. """ filepath, filename = os.path.split(self.filename) filename, _ = os.path.splitext(filename) global oldfileinfo oldfileinfo = self.filename p("self.filename: %s" % (self.filename)) newpath = os.path.join(filepath, newName) if os.path.isfile(newpath): # If the destination exists, raise exception unless force is True if not force: raise OSError("File %s already exists, not forcefully renaming %s" % ( newpath, self.filename)) os.rename(self.filename, newpath) self.filename = newpath
def delete_file(fpath): """On OS X: Trashes a path using the Finder, via OS X's Scripting Bridge. On other platforms: unlinks file. """ try: from AppKit import NSURL from ScriptingBridge import SBApplication except ImportError: p("Deleting %s" % fpath) log().debug("Deleting %r" % fpath) os.unlink(fpath) else: p("Trashing %s" % fpath) log().debug("Trashing %r" % fpath) targetfile = NSURL.fileURLWithPath_(fpath) finder = SBApplication.applicationWithBundleIdentifier_("com.apple.Finder") items = finder.items().objectAtLocation_(targetfile) items.delete()
def main(): """Parses command line arguments, displays errors from tvnamer in terminal """ opter = cliarg_parser.getCommandlineParser(defaults) opts, args = opter.parse_args() args = [x.decode("utf-8") for x in args] if opts.verbose: logging.basicConfig( level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") else: logging.basicConfig() # If a config is specified, load it, update the defaults using the loaded # values, then reparse the options with the updated defaults. default_configuration = os.path.expanduser("~/.tvnamer.json") if opts.loadconfig is not None: # Command line overrides loading ~/.tvnamer.json configToLoad = opts.loadconfig elif os.path.isfile(default_configuration): # No --config arg, so load default config if it exists configToLoad = default_configuration else: # No arg, nothing at default config location, don't load anything configToLoad = None if configToLoad is not None: p("Loading config: %s" % (configToLoad)) try: loadedConfig = json.load(open(configToLoad)) except ValueError, e: p("Error loading config: %s" % e) opter.exit(1) else: # Config loaded, update optparser's defaults and reparse defaults.update(loadedConfig) opter = cliarg_parser.getCommandlineParser(defaults) opts, args = opter.parse_args()
def main(): """Parses command line arguments, displays errors from tvnamer in terminal """ opter = cliarg_parser.getCommandlineParser(defaults) opts, args = opter.parse_args() args = [x.decode("utf-8") for x in args] if opts.verbose: logging.basicConfig( level = logging.DEBUG, format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s") else: logging.basicConfig() # If a config is specified, load it, update the defaults using the loaded # values, then reparse the options with the updated defaults. default_configuration = os.path.expanduser("~/.tvnamer.json") if opts.loadconfig is not None: # Command line overrides loading ~/.tvnamer.json configToLoad = opts.loadconfig elif os.path.isfile(default_configuration): # No --config arg, so load default config if it exists configToLoad = default_configuration else: # No arg, nothing at default config location, don't load anything configToLoad = None if configToLoad is not None: p("Loading config: %s" % (configToLoad)) try: loadedConfig = json.load(open(configToLoad)) except ValueError, e: p("Error loading config: %s" % e) opter.exit(1) else: # Config loaded, update optparser's defaults and reparse defaults.update(loadedConfig) opter = cliarg_parser.getCommandlineParser(defaults) opts, args = opter.parse_args()
def newName(self, newName, force = False): """Renames a file, keeping the path the same. """ filepath, filename = os.path.split(self.filename) filename, _ = os.path.splitext(filename) newpath = os.path.join(filepath, newName) if os.path.isfile(newpath): # If the destination exists, raise exception unless force is True if not force: raise OSError("File %s already exists, not forcefully renaming %s" % ( newpath, self.filename)) if Config['link_files_enable']: p("Creating hard link") os.link(self.filename, newpath) else: os.rename(self.filename, newpath) self.filename = newpath
def linkPath(self, new_path = None, new_fullpath = None, force = False, always_copy = False, always_move = False, create_dirs = True, getPathPreview = False): # Implement getPathPreview, just to respect the API if getPathPreview: return self.newPath(new_path, new_fullpath, force, always_copy, always_move, create_dirs, getPathPreview) # Grab the pathPreview from newPath to determine where we need to link to new_fullpath = self.newPath(new_path, new_fullpath, force, always_copy, always_move, create_dirs, True) new_dir = os.path.dirname(new_fullpath) # And then restore our faked filename, if one exists if self.srcFilename is not None: self.filename = self.srcFilename self.srcFilename = None if create_dirs: p("Creating directory %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise
def tvnamer(paths): """Main tvnamer function, takes an array of paths, does stuff. """ p("#" * 20) p("# Starting tvnamer") episodes_found = [] for cfile in findFiles(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename, e: warn("Invalid filename: %s" % e) else: if episode.seriesname is None and Config['force_name'] is None: warn("Parsed filename did not contain series name (and --name not specified), skipping: %s" % cfile) else: episodes_found.append(episode)
def rename(self, new_fullpath, force=False, always_copy=False, always_move=False, leave_symlink=False, create_dirs=True): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied, and the original only deleted if always_move is True. If the target file already exists, it will raise OSError unless force is True. If it was moved, a symlink will be left behind with the original name pointing to the file's new destination if leave_symlink is True. """ new_dir = os.path.dirname(new_fullpath) if create_dirs: p("Creating directory %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise
def tvnamer(paths): """Main tvnamer function, takes an array of paths, does stuff. """ p("#" * 20) p("# Starting tvnamer") episodes_found = [] for cfile in findFiles(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename, e: warn("Invalid filename: %s" % e) else: if episode.seriesname is None and Config['force_name'] is None and Config['series_id'] is None: warn("Parsed filename did not contain series name (and --name or --series-id not specified), skipping: %s" % cfile) else: episodes_found.append(episode)
def newPath(self, new_path=None, new_fullpath=None, force=False, always_copy=False, always_move=False, leave_symlink=False, create_dirs=True, getPathPreview=False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied, and the original only deleted if always_move is True. If the target file already exists, it will raise OSError unless force is True. If it was moved, a symlink will be left behind with the original name pointing to the file's new destination if leave_symlink is True. """ if always_copy and always_move: raise ValueError( "Both always_copy and always_move cannot be specified") if (new_path is None and new_fullpath is None) or (new_path is not None and new_fullpath is not None): raise ValueError("Specify only new_dir or new_fullpath") old_dir, old_filename = os.path.split(self.filename) if new_path is not None: # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) else: # Join new filepath to old one (to handle realtive dirs) new_fullpath = os.path.abspath(os.path.join(old_dir, new_fullpath)) new_dir = os.path.dirname(new_fullpath) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise else: p("Created directory %s" % new_dir)
def confirm(question, options, default="y"): """Takes a question (string), list of options and a default value (used when user simply hits enter). Asks until valid option is entered. """ # Highlight default option with [ ] options_str = [] for x in options: if x == default: x = "[%s]" % x if x != '': options_str.append(x) options_str = "/".join(options_str) while True: p(question) p("(%s) " % (options_str), end="") try: ans = raw_input().strip() except KeyboardInterrupt, errormsg: p("\n", errormsg) raise UserAbort(errormsg) if ans in options: return ans elif ans == '': return default
def confirm(question, options, default = "y"): """Takes a question (string), list of options and a default value (used when user simply hits enter). Asks until valid option is entered. """ # Highlight default option with [ ] options_str = [] for x in options: if x == default: x = "[%s]" % x if x != '': options_str.append(x) options_str = "/".join(options_str) while True: p(question) p("(%s) " % (options_str), end="") try: ans = raw_input().strip() except KeyboardInterrupt, errormsg: p("\n", errormsg) raise UserAbort(errormsg) if ans in options: return ans elif ans == '': return default
def newName(self, newName, force = False, leave_symlink = False): """Renames a file, keeping the path the same. """ filepath, filename = os.path.split(self.filename) filename, _ = os.path.splitext(filename) newpath = os.path.join(filepath, newName) if os.path.isfile(newpath): # If the destination exists, raise exception unless force is True if not force: raise OSError("File %s already exists, not forcefully renaming %s" % ( newpath, self.filename)) os.rename(self.filename, newpath) # Leave a symlink behind if configured to do so if leave_symlink: p("symlink %s to %s" % (self.filename, newpath)) os.symlink(newpath, self.filename) self.filename = newpath
def tvnamer(paths): """Main tvnamer function, takes an array of paths, does stuff. """ # Warn about move_files function if Config['move_files_enable']: import warnings warnings.warn( "The move_files feature is still under development. " "Be very careful with it.\n" "It has not been heavily tested, and is not recommended for " "general use yet.") if Config['force_name'] is not None: import warnings warnings.warn( "The --name argument is a temporary solution, and will" "be removed at some point in the future. Do no depend on it") p("#" * 20) p("# Starting tvnamer") episodes_found = [] for cfile in findFiles(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename, e: warn("Invalid filename: %s" % e) else: if episode.seriesname is None: warn( "Parsed filename did not contain series name, skipping: %s" % cfile) else: episodes_found.append(episode)
def processFile(tvdb_instance, episode): """Gets episode name, prompts user for input """ p("#" * 20) p("# Processing file: %s" % episode.fullfilename) if len(Config['input_filename_replacements']) > 0: replaced = applyCustomInputReplacements(episode.fullfilename) p("# With custom replacements: %s" % (replaced)) p("# Detected series: %s (%s)" % (episode.seriesname, episode.number_string())) try: correctedSeriesName, epName = getEpisodeName(tvdb_instance, episode) except (DataRetrievalError, ShowNotFound), errormsg: if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return else: warn(errormsg)
def processFile(comicvine_instance, issue): """Gets issue name, prompts user for input """ p("#" * 20) p("# Processing file: %s" % issue.fullfilename) if len(Config['input_filename_replacements']) > 0: replaced = applyCustomInputReplacements(issue.fullfilename) p("# With custom replacements: %s" % (replaced)) p("# Detected series: %s (issue: %s)" % (issue.seriesname, ", ".join([str(x) for x in issue.issuenumbers]))) try: correctedSeriesName, issName = getIssueName(comicvine_instance, issue) except (DataRetrievalError, SeriesNotFound), errormsg: if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return else: warn(errormsg)
def processFile(comicvine_instance, issue): """Gets issue name, prompts user for input """ p("#" * 20) p("# Processing file: %s" % issue.fullfilename) if len(Config['input_filename_replacements']) > 0: replaced = applyCustomInputReplacements(issue.fullfilename) p("# With custom replacements: %s" % (replaced)) p("# Detected volume: %s (issue: %s)" % ( issue.volumename, ", ".join([str(x) for x in issue.issuenumbers]))) try: correctedvolumeName, issName = getIssueName(comicvine_instance, issue) except (DataRetrievalError, volumeNotFound), errormsg: if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return else: warn(errormsg)
def newPath(self, new_path, force=False, always_copy=False, always_move=False, create_dirs=True, getPathPreview=False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied. If the target file already exists, it will raise OSError unless force is True. """ if always_copy and always_move: raise ValueError( "Both always_copy and always_move cannot be specified") old_dir, old_filename = os.path.split(self.filename) # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: p("Creating %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise
def newPath(self, new_path, force = False, always_copy = False, always_move = False, create_dirs = True, getPathPreview = False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied. If the target file already exists, it will raise OSError unless force is True. """ if always_copy and always_move: raise ValueError("Both always_copy and always_move cannot be specified") if Config["link_files_enable"] and always_copy: warn("WARNING: having link_files_enable and always_copy both True can lead to 3 copies of a file: the original, a hard link and a copy") old_dir, old_filename = os.path.split(self.filename) # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: p("Creating %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise
def getNewFullPath(self): """ Generates final fullPath, with all replacements, formatting etc. It's ready to pass it to Renamer.rename(). """ epdata = self.getepdata() newName = self.getFormatString() % epdata if len(Config['output_filename_replacements']) > 0: p("Before custom output replacements: '%s'" % newName) newName = applyCustomOutputReplacements(newName) p("After custom output replacements: '%s'" % newName) if self.eptype == 'dated': newPath = Config['move_files_destination_date'] % epdata else: newPath = Config['move_files_destination'] % epdata if Config['move_files_destination_is_filepath']: newPath, newName = os.path.split(newPath) # make newName lowercase if specified in config if Config['lowercase_filename']: newName = newName.lower() # make sure the filename is valid newName = makeValidFilename(newName) # Join new filepath to old one (to handle realtive dirs) oldPath = os.path.dirname(self.fullpath) newFullPath = os.path.abspath(os.path.join(oldPath, newPath, newName)) # apply full-path replacements if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: '%s'" % (newFullPath)) newFullPath = applyCustomFullpathReplacements(newFullPath) return newFullPath
def symlink_file(target, name): p("symlink %s to %s" % (name, target)) os.symlink(target, name)
def comicnamer(paths): """Main comicnamer function, takes an array of paths, does stuff. """ # Warn about move_files function if Config['move_files_enable']: import warnings warnings.warn( "The move_files feature is still under development. " "Be very careful with it.\n" "It has not been heavily tested, and is not recommended for " "general use yet.") p("#" * 20) p("# Starting comicnamer") issues_found = [] for cfile in findFiles(paths): cfile = cfile.decode("utf-8") parser = FileParser(cfile) try: issue = parser.parse() except InvalidFilename: warn("Invalid filename %s" % cfile) else: issues_found.append(issue) if len(issues_found) == 0: raise NoValidFilesFoundError() p("# Found %d issue" % len(issues_found) + ("s" * (len(issues_found) > 1))) # Sort issues by series name and issue number issues_found.sort(key=lambda x: (x.seriesname, x.issuenumbers)) comicvine_instance = Comicvine(interactive=not Config['select_first']) for issue in issues_found: processFile(comicvine_instance, issue) p('') p("#" * 20) p("# Done")
def tvnamer(paths): """Main tvnamer function, takes an array of paths, does stuff. """ # Warn about move_files function if Config['move_files_enable']: import warnings warnings.warn("The move_files feature is still under development. " "Be very careful with it.\n" "It has not been heavily tested, and is not recommended for " "general use yet.") p("#" * 20) p("# Starting tvnamer") episodes_found = [] for cfile in findFiles(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename: warn("Invalid filename %s" % cfile) else: episodes_found.append(episode) if len(episodes_found) == 0: raise NoValidFilesFoundError() p("# Found %d episode" % len(episodes_found) + ("s" * (len(episodes_found) > 1))) # Sort episodes by series name, season and episode number episodes_found.sort(key = lambda x: x.sortable_info()) tvdb_instance = Tvdb( interactive=not Config['select_first'], search_all_languages = Config['search_all_languages'], language = Config['language']) for episode in episodes_found: processFile(tvdb_instance, episode) p('') p("#" * 20) p("# Done")
class Renamer(object): """Deals with renaming of files """ def __init__(self, filename): self.filename = os.path.abspath(filename) def newPath(self, new_path=None, new_fullpath=None, force=False, always_copy=False, always_move=False, leave_symlink=False, create_dirs=True, getPathPreview=False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied, and the original only deleted if always_move is True. If the target file already exists, it will raise OSError unless force is True. If it was moved, a symlink will be left behind with the original name pointing to the file's new destination if leave_symlink is True. """ if always_copy and always_move: raise ValueError( "Both always_copy and always_move cannot be specified") if (new_path is None and new_fullpath is None) or (new_path is not None and new_fullpath is not None): raise ValueError("Specify only new_dir or new_fullpath") old_dir, old_filename = os.path.split(self.filename) if new_path is not None: # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) else: # Join new filepath to old one (to handle realtive dirs) new_fullpath = os.path.abspath(os.path.join(old_dir, new_fullpath)) new_dir = os.path.dirname(new_fullpath) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise else: p("Created directory %s" % new_dir) if os.path.isfile(new_fullpath): # If the destination exists, raise exception unless force is True if not force: raise OSError( "File %s already exists, not forcefully moving %s" % (new_fullpath, self.filename)) if same_partition(self.filename, new_dir): if always_copy: # Same partition, but forced to copy copy_file(self.filename, new_fullpath) else: # Same partition, just rename the file to move it rename_file(self.filename, new_fullpath) # Leave a symlink behind if configured to do so if leave_symlink: symlink_file(new_fullpath, self.filename) else: # File is on different partition (different disc), copy it copy_file(self.filename, new_fullpath) if always_move: # Forced to move file, we just trash old file p("Deleting %s" % (self.filename)) delete_file(self.filename) # Leave a symlink behind if configured to do so if leave_symlink: symlink_file(new_fullpath, self.filename) self.filename = new_fullpath
except (SeasonNotFound, EpisodeNotFound, EpisodeNameNotFound), errormsg: # Show was found, so use corrected series name if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return warn(errormsg) episode.seriesname = correctedSeriesName else: episode.seriesname = correctedSeriesName episode.episodename = epName cnamer = Renamer(episode.fullpath) newName = episode.generateFilename() p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']): p("Before custom output replacements: %s" % (newName)) # Only apply to filename, not extension newName, newExt = os.path.splitext(newName) newName = applyCustomOutputReplacements(newName) newName = newName + newExt p("New filename: %s" % newName) if Config['always_rename']: doRenameFile(cnamer, newName) if Config['move_files_enable']: doMoveFile(cnamer = cnamer, destDir = getDestinationFolder(episode))
def symlink_file(target, name): """Create symbolic link named 'name' pointing to 'target'. """ p("Creating symlink %s to %s" % (name, target)) log().debug("Creating symlink %r to %r" % (name, target)) os.symlink(target, name)
def warn(text): """Displays message to sys.stdout """ p(text, file=sys.stderr)
def copy_file(old, new): p("copy %s to %s" % (old, new)) shutil.copyfile(old, new) shutil.copystat(old, new)
# For atomic move, restore self.filename to it's real value here if self.srcFilename is not None: self.filename = self.srcFilename self.srcFilename = None if os.path.isfile(new_fullpath): # If the destination exists, raise exception unless force is True if not force: raise OSError("File %s already exists, not forcefully moving %s" % ( new_fullpath, self.filename)) if same_partition(self.filename, new_dir): if always_copy: # Same partition, but forced to copy p("copy %s to %s" % (self.filename, new_fullpath)) shutil.copyfile(self.filename, new_fullpath) else: # Same partition, just rename the file to move it p("move %s to %s" % (self.filename, new_fullpath)) os.rename(self.filename, new_fullpath) else: # File is on different partition (different disc), copy it p("copy %s to %s" % (self.filename, new_fullpath)) shutil.copyfile(self.filename, new_fullpath) if always_move: # Forced to move file, we just trash old file p("Deleting %s" % (self.filename)) delete_file(self.filename) # Finally, if we were asked to make a link back and there's nothing left behind, create the link
warn(errormsg) cnamer = Renamer(episode.fullpath) shouldRename = False if Config["move_files_only"]: newName = episode.fullfilename shouldRename = True else: newName = episode.generateFilename() if newName == episode.fullfilename: p("#" * 20) p("Existing filename is correct: %s" % episode.fullfilename) p("#" * 20) shouldRename = True else: p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']) > 0: # Show filename without replacements p("Before custom output replacements: %s" % (episode.generateFilename(preview_orig_filename = False))) p("New filename: %s" % newName)
return else: warn(errormsg) except (SeasonNotFound, EpisodeNotFound, EpisodeNameNotFound), errormsg: # Show was found, so use corrected series name if Config['always_rename'] and Config['skip_file_on_error']: warn("Skipping file due to error: %s" % errormsg) return warn(errormsg) cnamer = Renamer(episode.fullpath) newName = episode.generateFilename() if newName == episode.fullfilename: p("#" * 20) p("Skipping: existing filename is correct: %s" % episode.fullfilename) p("#" * 20) else: p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']) > 0: # Show filename without replacements p("Before custom output replacements: %s" % (episode.generateFilename(preview_orig_filename=False))) p("New filename: %s" % newName) if Config['always_rename']: doRenameFile(cnamer, newName)
def comicnamer(paths): """Main comicnamer function, takes an array of paths, does stuff. """ # Warn about move_files function if Config['move_files_enable']: import warnings warnings.warn("The move_files feature is still under development. " "Be very careful with it.\n" "It has not been heavily tested, and is not recommended for " "general use yet.") p("#" * 20) p("# Starting comicnamer") issues_found = [] for cfile in findFiles(paths): cfile = cfile.decode("utf-8") parser = FileParser(cfile) try: issue = parser.parse() except InvalidFilename: warn("Invalid filename %s" % cfile) else: issues_found.append(issue) if len(issues_found) == 0: raise NoValidFilesFoundError() p("# Found %d issue" % len(issues_found) + ("s" * (len(issues_found) > 1))) # Sort issues by volume name and issue number issues_found.sort(key = lambda x: (x.volumename, x.issuenumbers)) comicvine_instance = Comicvine( interactive=not Config['select_first']) for issue in issues_found: processFile(comicvine_instance, issue) p('') p("#" * 20) p("# Done")
def warn(text): """Displays message to sys.stdout """ p(text, file = sys.stderr)
warn(errormsg) cnamer = Renamer(episode.fullpath) shouldRename = False if Config["move_files_only"]: newName = episode.fullfilename shouldRename = True else: newName = episode.generateFilename() if newName == episode.fullfilename: p("#" * 20) p("Existing filename is correct: %s" % episode.fullfilename) p("#" * 20) shouldRename = True else: p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']) > 0: # Show filename without replacements p("Before custom output replacements: %s" % (episode.generateFilename(preview_orig_filename=False))) p("New filename: %s" % newName)
class Renamer(object): """Deals with renaming of files """ def __init__(self, filename): self.filename = os.path.abspath(filename) def newName(self, newName, force=False): """Renames a file, keeping the path the same. """ filepath, filename = os.path.split(self.filename) filename, _ = os.path.splitext(filename) newpath = os.path.join(filepath, newName) if os.path.isfile(newpath): # If the destination exists, raise exception unless force is True if not force: raise OSError( "File %s already exists, not forcefully renaming %s" % (newpath, self.filename)) os.rename(self.filename, newpath) self.filename = newpath def newPath(self, new_path, force=False, always_copy=False, always_move=False, create_dirs=True, getPathPreview=False): """Moves the file to a new path. If it is on the same partition, it will be moved (unless always_copy is True) If it is on a different partition, it will be copied. If the target file already exists, it will raise OSError unless force is True. """ if always_copy and always_move: raise ValueError( "Both always_copy and always_move cannot be specified") old_dir, old_filename = os.path.split(self.filename) # Join new filepath to old one (to handle realtive dirs) new_dir = os.path.abspath(os.path.join(old_dir, new_path)) # Join new filename onto new filepath new_fullpath = os.path.join(new_dir, old_filename) if len(Config['move_files_fullpath_replacements']) > 0: p("Before custom full path replacements: %s" % (new_fullpath)) new_fullpath = applyCustomFullpathReplacements(new_fullpath) new_dir = os.path.dirname(new_fullpath) p("New path: %s" % new_fullpath) if getPathPreview: return new_fullpath if create_dirs: p("Creating %s" % new_dir) try: os.makedirs(new_dir) except OSError, e: if e.errno != 17: raise if os.path.isfile(new_fullpath): # If the destination exists, raise exception unless force is True if not force: raise OSError( "File %s already exists, not forcefully moving %s" % (new_fullpath, self.filename)) if same_partition(self.filename, new_dir): if always_copy: # Same partition, but forced to copy p("copy %s to %s" % (self.filename, new_fullpath)) shutil.copyfile(self.filename, new_fullpath) else: # Same partition, just rename the file to move it p("move %s to %s" % (self.filename, new_fullpath)) os.rename(self.filename, new_fullpath) else: # File is on different partition (different disc), copy it p("copy %s to %s" % (self.filename, new_fullpath)) shutil.copyfile(self.filename, new_fullpath) if always_move: # Forced to move file, we just trash old file p("Deleting %s" % (self.filename)) delete_file(self.filename) self.filename = new_fullpath
return else: warn(errormsg) except (SeasonNotFound, EpisodeNotFound, EpisodeNameNotFound), errormsg: # Show was found, so use corrected series name if Config['always_rename'] and Config['skip_file_on_error']: warn("Skipping file due to error: %s" % errormsg) return warn(errormsg) cnamer = Renamer(episode.fullpath) newName = episode.generateFilename() if newName == episode.fullfilename: p("#" * 20) p("Skipping: existing filename is correct: %s" % episode.fullfilename) p("#" * 20) else: p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']) > 0: # Show filename without replacements p("Before custom output replacements: %s" % (episode.generateFilename(preview_orig_filename = False))) p("New filename: %s" % newName) if Config['always_rename']: doRenameFile(cnamer, newName) if Config['move_files_enable']:
except (IssueNotFound, IssueNameNotFound), errormsg: # Series was found, so use corrected series name if Config['always_rename'] and Config['skip_file_on_error'] is True: warn("Skipping file due to error: %s" % errormsg) return warn(errormsg) issue.seriesname = correctedSeriesName else: issue.seriesname = correctedSeriesName issue.issuename = issName cnamer = Renamer(issue.fullpath) newName = issue.generateFilename() p("#" * 20) p("Old filename: %s" % issue.fullfilename) if len(Config['output_filename_replacements']): p("Before custom output replacements: %s" % (newName)) # Only apply to filename, not extension newName, newExt = os.path.splitext(newName) newName = applyCustomOutputReplacements(newName) newName = newName + newExt p("New filename: %s" % newName) if Config['always_rename']: doRenameFile(cnamer, newName) if Config['move_files_enable']: doMoveFile(cnamer=cnamer, destDir=getDestinationFolder(issue))