Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions tvnamer/cliarg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ def getCommandlineParser(defaults):
g.add_option("-m", "--move", action="store_true", dest="move_files_enable", help = "Move files to destination specified in config or with --movedestination argument")
g.add_option("--not-move", action="store_false", dest="move_files_enable", help = "Files will remain in current directory")

g.add_option("-C", "--copy", action="store_true", dest="always_copy", help = "Copy files instead of renaming")
g.add_option("--not-copy", action="store_false", dest="always_copy", help = "Files will not be copied")

g.add_option("-l", "--link", action="store_true", dest="always_hardlink", help = "Hardlink instead of renaming")
g.add_option("--not-link", action="store_false", dest="always_hardlink", help = "Files will not all be hardlink")

g.add_option("--force-move", action="store_true", dest = "overwrite_destination_on_move", help = "Force move and potentially overwrite existing files in destination folder")
g.add_option("--force-rename", action="store_true", dest = "overwrite_destination_on_rename", help = "Force rename source file")

Expand Down
14 changes: 13 additions & 1 deletion tvnamer/config_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
'batch': False,

# Fail if error finding show data (thetvdb.com is down etc)
# Only functions when always_rename is True
# Only functions when always_rename or always_hardlink are True
'skip_file_on_error': True,

# Fail if error finding show data (thetvdb.com is down etc)
Expand Down Expand Up @@ -146,6 +146,18 @@
# Allow user to copy files to specified move location without renaming files.
'move_files_only': False,

# Force the move-files feature to always copy the file.
'always_copy': False,

# If True, instead of copying files to be copied on the same partition,
# a hardlink will be created.
'prefer_hardlink_to_copy': False,

# Forces files to be hard-linked. Will raise an error if it is not possible
# (i.e. different partition, unsuported, etc.).
# This option is incompatible with always_move
'always_hardlink': False,

# Patterns to parse input filenames with
'filename_patterns': [
# [group] Show - 01-02 [crc]
Expand Down
53 changes: 28 additions & 25 deletions tvnamer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def doMoveFile(cnamer, destDir = None, destFilepath = None, getPathPreview = Fal
if (destDir is None and destFilepath is None) or (destDir is not None and destFilepath is not None):
raise ValueError("Specify only destDir or destFilepath")

if not Config['move_files_enable']:
if not (Config['move_files_enable'] or Config['always_copy'] or Config['always_hardlink']):
raise ValueError("move_files feature is disabled but doMoveFile was called")

if Config['move_files_destination'] is None:
Expand All @@ -110,6 +110,8 @@ def doMoveFile(cnamer, destDir = None, destFilepath = None, getPathPreview = Fal
new_path = destDir,
new_fullpath = destFilepath,
always_move = Config['always_move'],
always_copy = Config['always_copy'],
always_hardlink=Config['always_hardlink'],
leave_symlink = Config['leave_symlink'],
getPathPreview = getPathPreview,
force = Config['overwrite_destination_on_move'])
Expand Down Expand Up @@ -224,12 +226,14 @@ def processFile(tvdb_instance, episode):
p("%s will be moved to %s" % (newName, getMoveDestination(episode)))
return
elif Config['always_rename']:
doRenameFile(cnamer, newName)
if Config['move_files_enable']:
if Config['move_files_enable'] or Config['always_copy'] or Config['always_hardlink']:
if Config['move_files_destination_is_filepath']:
doMoveFile(cnamer = cnamer, destFilepath = getMoveDestination(episode))
else:
doMoveFile(cnamer = cnamer, destDir = getMoveDestination(episode))
doRenameFile(cnamer, newName)
else:
doRenameFile(cnamer, newName)
return

ans = confirm("Rename?", options = ['y', 'n', 'a', 'q'], default = 'y')
Expand All @@ -249,31 +253,30 @@ def processFile(tvdb_instance, episode):
else:
p("Invalid input, skipping")

if shouldRename:
doRenameFile(cnamer, newName)
if shouldRename:
if Config['move_files_enable'] or Config['always_copy'] or Config['always_hardlink']:
newPath = getMoveDestination(episode)
if Config['dry_run']:
p("%s will be moved to %s" % (newName, getMoveDestination(episode)))
return

if shouldRename and Config['move_files_enable']:
newPath = getMoveDestination(episode)
if Config['dry_run']:
p("%s will be moved to %s" % (newName, getMoveDestination(episode)))
return
if Config['move_files_destination_is_filepath']:
doMoveFile(cnamer = cnamer, destFilepath = newPath, getPathPreview = True)
else:
doMoveFile(cnamer = cnamer, destDir = newPath, getPathPreview = True)

if Config['move_files_destination_is_filepath']:
doMoveFile(cnamer = cnamer, destFilepath = newPath, getPathPreview = True)
else:
doMoveFile(cnamer = cnamer, destDir = newPath, getPathPreview = True)
if not Config['batch'] and Config['move_files_confirmation']:
ans = confirm("Move file?", options = ['y', 'n', 'q'], default = 'y')
else:
ans = 'y'

if not Config['batch'] and Config['move_files_confirmation']:
ans = confirm("Move file?", options = ['y', 'n', 'q'], default = 'y')
else:
ans = 'y'

if ans == 'y':
p("Moving file")
doMoveFile(cnamer, newPath)
elif ans == 'q':
p("Quitting")
raise UserAbort("user exited with q")
if ans == 'y':
p("Moving file")
doMoveFile(cnamer, newPath)
elif ans == 'q':
p("Quitting")
raise UserAbort("user exited with q")
doRenameFile(cnamer, newName)


def findFiles(paths):
Expand Down
25 changes: 21 additions & 4 deletions tvnamer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,14 +1019,22 @@ def symlink_file(target, name):
os.symlink(target, name)


def hardlink_file(old, new):
"""Creates a hard link from the 'old' file at a 'new' target path with same permissions
"""
p("hardlink %s to %s" % (old, new))
os.link(old, new)
shutil.copystat(old, new)


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):
def newPath(self, new_path = None, new_fullpath = None, force = False, always_copy = False, prefer_hardlink_to_copy = False, always_move = False, always_hardlink = 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)
Expand All @@ -1037,8 +1045,9 @@ def newPath(self, new_path = None, new_fullpath = None, force = False, always_co
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 (always_copy, always_move, always_hardlink).count(True) > 1:
raise ValueError("Different incompatible renaming operation cannot be specified: "
"always_copy or always_move or always_hardlink")

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")
Expand Down Expand Up @@ -1086,7 +1095,15 @@ def newPath(self, new_path = None, new_fullpath = None, force = False, always_co

if always_copy:
# Same partition, but forced to copy
copy_file(self.filename, new_fullpath)
if Config['prefer_hardlink_to_copy']:
try:
hardlink_file(self.filename, new_fullpath)
except OSError:
copy_file(self.filename, new_fullpath)
else:
copy_file(self.filename, new_fullpath)
elif always_hardlink:
hardlink_file(self.filename, new_fullpath)
else:
# Same partition, just rename the file to move it
rename_file(self.filename, new_fullpath)
Expand Down