Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 1be8f4445f
executable file 321 lines (265 sloc) 10.055 kb
#!/usr/bin/env python
#encoding:utf-8
"""
autoPathTv.py
Use at your own risk. "It works for me".
This parses "Show Name - [01x23] - Episode Name.avi"
filenames and automatically copies them to
/Volumes/aodDrive/video/tv/Show Name/season 1/
(obviously the path is changable, as is in the input
format if you change the reges)
"""
import os, sys, re
from optparse import OptionParser
import shutil
config = {}
regex_config={}
##############################################
# Path configs
# Where to move the files
config['target_path'] = "/Volumes/ionDrive/video/tv/%(file_showname)s/season %(seasno)s/"
##############################################
# Regex configs
# Character class for valid episode/show names.
# Example: [a-zA-Z0-9\-'\ ]
regex_config['valid_in_names'] = "[\w\(\).,\[\]'\ \-?]"
config['name_parse'] = [
re.compile("^(%(valid_in_names)s+) - \[(\d{2})x(\d{2})\] - (%(valid_in_names)s+)$" % (regex_config)),
re.compile("^(%(valid_in_names)s+) - \[(\d{2})x(\d{2}-\d{2})\] - (%(valid_in_names)s+)$" % (regex_config)),
re.compile("^(%(valid_in_names)s+) - \[(\d{2})x(Special\d{1,2})\] - (%(valid_in_names)s+)$" % (regex_config)),
re.compile("^(%(valid_in_names)s+) - \[(\d{2})xExtra(\d{1,2})\] - (%(valid_in_names)s+)$" % (regex_config)),
re.compile("^(%(valid_in_names)s+) - \[(\d{2})] - (%(valid_in_names)s+)$" % (regex_config)),
]
# end configs
##############################################
def colour(text,colour="red"):
nocolour=False
if nocolour: # Colour no supported, return plain text
return text
#end if
c = {'red':'[31m',
'green':'[32m',
'blue':'[34m',
}
CLR=chr(27)+'[0m'
if not colour in c.keys():
raise ValueError("Invalid colour")
else:
return chr(27)+c[colour] + text + CLR
#end if
#end colour
class ProgressBar:
"""From http://code.activestate.com/recipes/168639/"""
def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
self.progBar = "[]" # This holds the progress bar string
self.min = minValue
self.max = maxValue
self.span = maxValue - minValue
self.width = totalWidth
self.amount = 0 # When amount == max, we are 100% done
self.updateAmount(0) # Build progress bar string
def updateAmount(self, newAmount = 0):
if newAmount < self.min: newAmount = self.min
if newAmount > self.max: newAmount = self.max
self.amount = newAmount
# Figure out the new percent done, round to an integer
diffFromMin = float(self.amount - self.min)
percentDone = (diffFromMin / float(self.span)) * 100.0
percentDone = round(percentDone)
percentDone = int(percentDone)
# Figure out how many hash bars the percentage should be
allFull = self.width - 2
numHashes = (percentDone / 100.0) * allFull
numHashes = int(round(numHashes))
# build a progress bar with hashes and spaces
self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"
# figure out where to put the percentage, roughly centered
percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
percentString = str(percentDone) + "%"
# slice the percentage into the bar
self.progBar = (self.progBar[0:percentPlace] + percentString
+ self.progBar[percentPlace+len(percentString):])
def __str__(self):
return str(self.progBar)
def copy_with_prog(src_file, dest_file, overwrite = False, block_size = 512):
if not overwrite:
if os.path.isfile(dest_file):
raise IOError("File exists, not overwriting")
# Open src and dest files, get src file size
src = open(src_file, "rb")
dest = open(dest_file, "wb")
src_size = os.stat(src_file).st_size
# Set progress bar
prgb = ProgressBar(totalWidth = 79, maxValue = src_size)
# Start copying file
cur_block_pos = 0 # a running total of current position
while True:
cur_block = src.read(block_size)
# Update progress bar
prgb.updateAmount(cur_block_pos)
cur_block_pos += block_size
sys.stdout.write(
'\r%s\r' % str(prgb)
)
# If it's the end of file
if not cur_block:
# ..write new line to prevent messing up terminal
sys.stderr.write('\n')
break
else:
# ..if not, write the block and continue
dest.write(cur_block)
#end while
# Close files
src.close()
dest.close()
# Check output file is same size as input one!
dest_size = os.stat(dest_file).st_size
if dest_size != src_size:
raise IOError(
"New file-size does not match original (src: %s, dest: %s)" % (
src_size, dest_size)
)
def findFiles(args):
"""
Takes a list of files/folders, grabs files inside them. Does not recurse
more than one level (if a folder is supplied, it will list files within)
"""
allfiles = []
for cfile in args:
if os.path.isdir(cfile):
for sf in os.listdir(cfile):
newpath = os.path.join(cfile, sf)
if os.path.isfile(newpath):
allfiles.append(newpath)
#end if isfile
#end for sf
elif os.path.isfile(cfile):
allfiles.append(cfile)
#end if isdir
#end for cfile
return allfiles
#end findFiles
def processNames(names, verbose=False):
"""
Takes list of names, runs them though the config['name_parse'] regexs
"""
allEps = []
for f in names:
filepath, filename = os.path.split( f )
filename, ext = os.path.splitext( filename )
# Remove leading . from extension
ext = ext.replace(".", "", 1)
for r in config['name_parse']:
match = r.match(filename)
if match:
showname, seasno, epno, epname = match.groups()
#remove ._- characters from name (- removed only if next to end of line)
showname = re.sub("[\._]|\-(?=$)", " ", showname).strip()
seasno, epno = int(seasno), int(epno)
if verbose:
print "*"*20
print "File:", filename
print "Pattern:", r.pattern
print "Showname:", showname
print "Seas:", seasno
print "Ep:", epno
print "*"*20
allEps.append({ 'file_showname':showname,
'seasno':seasno,
'epno':epno,
'filepath':filepath,
'filename':filename,
'ext':ext
})
break # Matched - to the next file!
else:
print "Invalid name: %s" % (f)
#end for r
#end for f
return allEps
#end processNames
def confirm(question="Rename files?"):
ans = None
while ans not in ["q", "quit"]:
print "y/n/a/q?",
ans = raw_input()
print ans
if ans.lower() in ["y", "yes"]:
return True
elif ans.lower() in ["n", "no"]:
return False
elif ans.lower() in ["a", "always"]:
return "always"
else:
sys.exit(1)
#end confirm
def make_path(path):
try:
os.makedirs(path)
except OSError:
print "Couldn't make path"
#end make_path
def does_file_exist(path):
try:
os.stat(path)
except OSError:
file_exists = False
else:
file_exists = True
return file_exists
def same_partition(f1, f2):
return os.stat(f1).st_dev == os.stat(f2).st_dev
###########################
def main():
parser = OptionParser(usage="%prog [options] <file or directories>")
parser.add_option("-a", "--always", dest = "always",
action="store_true", default = False,
help="Do not ask for confirmation before copying")
opts, args = parser.parse_args()
files = findFiles(args)
files = processNames(files)
# Warn if no files are found, then exit
if len(files) == 0:
print colour('No files found','red')
sys.exit(1)
#end if files == 0
for name in files:
oldfile = os.path.join(name['filepath'], name['filename']) + "." + name['ext']
newpath = config['target_path'] % name
newfile = os.path.join(newpath, name['filename']) + "." + name['ext']
print "Old path:", oldfile
print "New path:", newfile
ans=confirm()
if ans == "always": opts.always = True
if ans or opts.always:
make_path(newpath)
file_exists = does_file_exist(newfile)
if file_exists:
print "[!] File already exists, not copying"
else:
if same_partition(oldfile, newpath):
print "[*] Moving file"
try:
shutil.move(oldfile, newpath)
except Exception, errormsg:
print "[!] Error moving file! %s" % (errormsg)
#end try
else:
print "[*] Copying file"
try:
copy_with_prog(oldfile, newfile)
except Exception, errormsg:
print "[!] Error copying file! %s" % (errormsg)
else:
print "[*] ..done"
#end try
#end if same_partition
#end if not file_exists
else:
print "Skipping file"
#end if ans
#end for name in files
#end main
if __name__ == '__main__':
main()
Jump to Line
Something went wrong with that request. Please try again.