import sys
import errno
import shutil
-from optparse import OptionParser
+import logging
+import optparse
### Default Configuration Variables ###
DICT_FILE = os.path.join ('~','.config','animesorter2','animesorter.dict')
WORK_DIR = os.path.join ('~','downloads','usenet')
SORT_DIR = os.path.join ('/','data','Anime')
-TYPES_REGEX = '.*(avi|ogm|mkv|mp4|\d\d\d)$'
-
class AnimeSorter2:
def __init__(self, options):
self.options = options
+ self.dict = None
+
+ def __valid_dict_line (self, line):
+ if len(line) <= 0:
+ return False
+
+ if '=' not in line:
+ return False
+
+ # Comment lines are not really valid
+ if re.match ('^(\s*#.*|\s*)$', line):
+ return False
+
+ # Make sure that there is a definition and it is valid
+ try:
+ (regex, directory) = line.split('=')
+ regex = regex.strip()
+ directory = directory.strip()
+ except:
+ return False
+
+ # Make sure they have length
+ if len(regex) <= 0 or len(directory) <= 0:
+ return False
+
+ # I guess that it's valid now
+ return True
def parse_dict(self):
"""Parses a dictionary file containing the sort definitions in the form:
- DIRECTORY = PATTERN
+ REGEX_PATTERN = DIRECTORY
Returns a list of tuples of the form (compiled_regex, to_directory)"""
try:
f = open(self.options.dict_file, 'r', 0)
try:
- data = f.read()
+ raw_lines = f.readlines()
finally:
f.close()
except IOError:
- self.print_dict_fail (self.options.dict_file)
+ logging.critical ('Opening dictionary: %s FAILED' % self.options.dict_file)
sys.exit()
- ### Get a LIST containing each line in the file
- lines = [l for l in data.split('\n') if len(l) > 0]
-
- ### Remove comments / blank lines (zero length lines already removed above)
- regex = re.compile ('^\s*#.*$')
- lines = [l for l in lines if not re.match (regex, l)]
- regex = re.compile ('^\s*$')
- lines = [l for l in lines if not re.match (regex, l)]
-
- ### Split each line into a tuple, and strip each element of spaces
- result = self.split_lines(lines)
- result = [(re.compile(r), d) for r, d in result]
-
- ### Give some information about the dictionary we are using
- self.print_dict_suc (self.options.dict_file, len(result))
-
- return tuple(result)
-
- def split_lines(self, lines):
+ ### Find all of the valid lines in the file
+ valid_lines = [l for l in raw_lines if self.__valid_dict_line (l)]
+ # Set up variable for result
result = []
- for l in lines:
-
- try:
- r, d = l.split('=')
- r = r.strip()
- d = d.strip()
- except ValueError:
- self.print_dict_bad_line (l)
- continue
+ ### Split each line into a tuple, and strip each element of spaces
+ for l in valid_lines:
+ (regex, directory) = l.split('=')
+ regex = regex.strip()
+ directory = directory.strip()
- result.append((r, d))
+ # Fix up the directory if necessary
+ if directory[0] != '/':
+ directory = os.path.join (self.options.output_dir, directory)
- return result
+ # Fix up the regex
+ if regex[0] != '^':
+ regex = '^' + regex
- def get_matches(self, files, pattern):
- """get_matches(files, pattern):
+ if regex[-1] != '$':
+ regex += '$'
- files is type LIST
- pattern is type sre.SRE_Pattern
+ # Store the result
+ result.append ( (re.compile (regex), directory) )
- Returns a list of the files matching the pattern as type sre.SRE_Match."""
+ ### Give some information about the dictionary we are using
+ logging.info ('Successfully loaded %d records from %s\n' % \
+ (len(result), self.options.dict_file))
- matches = [m for m in files if pattern.search(m)]
- return matches
+ return tuple (result)
def as_makedirs (self, dirname):
"""Call os.makedirs(dirname), but check first whether we are in pretend
if not os.path.isdir (dirname):
if self.options.pretend:
- self.print_dir_create_pretend (dirname)
+ logging.info ('Will create directory %s' % dirname)
return 0
if self.get_user_choice ('Make directory?: %s' % (dirname, )):
try:
os.makedirs (dirname)
- self.print_dir_create_suc (dirname)
+ logging.info ('Created directory %s' % dirname)
except:
- self.print_dir_create_fail (dirname)
+ logging.critical ('Failed to create directory %s' % dirname)
return errno.EIO
return 0
dstname = os.path.join (todir, f)
if self.options.pretend:
- self.print_move_file_pretend (f, todir)
+ logging.info ('Will move %s to %s' % (f, todir))
return 0 # success
if self.get_user_choice ('Move file?: %s --> %s' % (srcname, dstname)):
try:
shutil.move (srcname, dstname)
- self.print_move_file_suc (f, todir)
+ logging.info ('Moved %s to %s' % (f, todir))
except:
- self.print_move_file_fail (f, todir)
+ logging.critical ('FAILED to move %s to %s' % (f, todir))
return errno.EIO
return 0
ret = 0
- ## Check for a non-default directory
- if todir[0] != '/':
- todir = os.path.join(self.options.output_dir, todir)
+ # Leave immediately if we have nothing to do
+ if len(files) <= 0:
+ return ret
## Create the directory if it doesn't exist
ret = self.as_makedirs (todir)
return ret
- def __dir_walker(self, dict, root, dirs, files):
+ def __dir_walker(self, rootdir, files):
- ## Get all of the files in the directory that are of the correct types
- types_re = re.compile(TYPES_REGEX, re.IGNORECASE)
- raw_matches = [f for f in files if types_re.match(f)]
-
- ### Loop through the dictionary and try to move everything that matches
- for regex, todir in dict:
- matches = self.get_matches(raw_matches, regex)
-
- ## Move the files if we've found some
- if len(matches) > 0:
- self.move_files(matches, root, todir)
+ for (r,d) in self.dict:
+ matches = [f for f in files if r.match(f)]
+ self.move_files (matches, rootdir, d)
def get_user_choice(self, prompt):
def main(self):
## Print the program's header
- self.print_prog_header ()
+ logging.info ('Regular Expression File Sorter (aka animesorter)')
+ logging.info ('=' * 80)
+ logging.info ('Copyright (c) 2005-2007, Ira W. Snyder (devel@irasnyder.com)')
+ logging.info ('This program is licensed under the GNU GPL v2')
+ logging.info ('')
## Parse the dictionary
- dict = self.parse_dict()
+ self.dict = self.parse_dict()
if self.options.recursive:
## Start walking through directories
for root, dirs, files in os.walk(self.options.start_dir):
- self.__dir_walker(dict, root, dirs, files)
+ self.__dir_walker(root, files)
else:
- self.__dir_walker(dict, self.options.start_dir,
- [d for d in os.listdir(self.options.start_dir) if os.path.isdir(d)],
- [f for f in os.listdir(self.options.start_dir) if os.path.isfile(f)])
-
- ############################################################################
- ### Functions for the printing system
- ############################################################################
-
- def print_prog_header(self):
- if self.options.quiet:
- return
-
- print 'Regular Expression File Sorter (aka animesorter)'
- print '================================================================================'
- print 'Copyright (c) 2005,2006, Ira W. Snyder (devel@irasnyder.com)'
- print 'All rights reserved.'
- print 'This program is licensed under the GNU GPL v2'
- print
-
- def print_move_file_suc(self, f, t):
- if self.options.quiet:
- return
-
- print 'Moved %s to %s' % (f, t)
-
- def print_move_file_fail(self, f, t):
- print 'FAILED to move %s to %s' % (f, t)
-
- def print_move_file_pretend (self, f, t):
- print 'Will move %s to %s' % (f, t)
-
- def print_dir_create_suc(self, d):
- if self.options.quiet:
- return
-
- print 'Created directory %s' % (d, )
-
- def print_dir_create_fail(self, d):
- print 'Failed to create directory %s' % (d, )
-
- def print_dir_create_pretend (self, d):
- print 'Will create directory %s' % (d, )
-
- def print_dict_suc(self, dic, num):
- if self.options.quiet:
- return
-
- print 'Successfully loaded %d records from %s\n' % (num, dic)
-
- def print_dict_fail(self, dic):
- print 'Opening dictionary: %s FAILED' % (dic, )
-
- def print_dict_bad_line(self, dic):
- print 'Bad line in dictionary: %s' % (dic, )
-
+ self.__dir_walker(self.options.start_dir, [f for f in
+ os.listdir(self.options.start_dir) if os.path.isfile(f)])
### MAIN IS HERE ###
def main():
+ # Set up the logger
+ logging.basicConfig (level=logging.INFO, format='%(message)s')
+
### Get the program options
- parser = OptionParser()
+ parser = optparse.OptionParser()
parser.add_option('-q', '--quiet', action='store_true', dest='quiet',
default=False, help="Don't print status messages to stdout")
parser.add_option('-d', '--dict', dest='dict_file', default=DICT_FILE,
options.start_dir = os.path.abspath(os.path.expanduser(options.start_dir))
options.output_dir = os.path.abspath(os.path.expanduser(options.output_dir))
+ # Change the loglevel if we're running in quiet mode
+ if options.quiet:
+ logging.getLogger().setLevel (logging.CRITICAL)
+
as = AnimeSorter2(options)
as.main()
if __name__ == '__main__':
main ()
-# vim: set ts=4 sw=4 sts=4 expandtab: