116 |
ira |
1 |
#!/usr/bin/python
|
|
|
2 |
|
|
|
3 |
import sys, os, glob, re, shutil
|
|
|
4 |
|
|
|
5 |
DICT_FILE = os.path.expanduser('~/bin/animesorter.dict')
|
|
|
6 |
WORK_DIR = os.path.expanduser('~/downloads/usenet')
|
|
|
7 |
SORT_DIR = '/data/Anime'
|
|
|
8 |
|
|
|
9 |
def parsedict(dict=DICT_FILE):
|
|
|
10 |
"""Parses a dictionary file containing the sort definitions in the form:
|
|
|
11 |
DIRECTORY = PATTERN
|
|
|
12 |
|
|
|
13 |
Returns a list of tuples, each containing the contents of one line."""
|
|
|
14 |
try:
|
|
|
15 |
f = open(dict, 'r', 0)
|
|
|
16 |
try:
|
|
|
17 |
data = f.read()
|
|
|
18 |
finally:
|
|
|
19 |
f.close()
|
|
|
20 |
except IOError:
|
|
|
21 |
print 'Error opening %s ... exiting!' % dict
|
|
|
22 |
sys.exit()
|
|
|
23 |
|
|
|
24 |
### Get a LIST containing each line in the file
|
|
|
25 |
lines = [l for l in data.split('\n') if len(l) > 0]
|
|
|
26 |
|
|
|
27 |
### Split each line into a tuple, and strip each element of spaces
|
|
|
28 |
tuples = [(k, v) for k, v in [l.split('=') for l in lines]]
|
|
|
29 |
tuples = [(k.strip() ,v.strip()) for k, v in tuples]
|
|
|
30 |
|
|
|
31 |
return tuple(tuples)
|
|
|
32 |
|
|
|
33 |
def getfiles(dir=WORK_DIR):
|
|
|
34 |
"""getfiles(dir):
|
|
|
35 |
|
|
|
36 |
dir is type STRING
|
|
|
37 |
|
|
|
38 |
Return a LIST of the files of type *.{avi,ogm,mkv} that are in the
|
|
|
39 |
directory given as a parameter."""
|
|
|
40 |
files = []
|
|
|
41 |
types = ('avi', 'ogm', 'mkv')
|
|
|
42 |
|
|
|
43 |
# Match every type in the type array
|
|
|
44 |
for t in types:
|
|
|
45 |
files.extend( glob.glob('%s/%s' % (dir, '*.%s' % t)) )
|
|
|
46 |
|
|
|
47 |
files = [f for d,f in [os.path.split(f) for f in files]]
|
|
|
48 |
return files
|
|
|
49 |
|
|
|
50 |
def getdirs(dir=WORK_DIR):
|
|
|
51 |
"""getdirs(dir):
|
|
|
52 |
|
|
|
53 |
dir is type STRING
|
|
|
54 |
|
|
|
55 |
Return a LIST of the subdirectories of the directory given as a parameter."""
|
|
|
56 |
dirs = [os.path.join(dir, d) for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
|
|
|
57 |
return dirs
|
|
|
58 |
|
|
|
59 |
def createpattern(string):
|
|
|
60 |
"""createpattern(string):
|
|
|
61 |
|
|
|
62 |
Returns a sre.SRE_Pattern created from the string given as a parameter."""
|
|
|
63 |
pattern = re.compile(string)
|
|
|
64 |
return pattern
|
|
|
65 |
|
|
|
66 |
def getmatches(files, pattern):
|
|
|
67 |
"""getmatches(files, pattern):
|
|
|
68 |
|
|
|
69 |
files is type LIST
|
|
|
70 |
pattern is type sre.SRE_Pattern
|
|
|
71 |
|
|
|
72 |
Returns a list of the files matching the pattern as type sre.SRE_Match."""
|
|
|
73 |
matches = [m for m in files if pattern.search(m)]
|
|
|
74 |
return matches
|
|
|
75 |
|
|
|
76 |
def fname_from_match(match):
|
|
|
77 |
"""fname_from_match(match):
|
|
|
78 |
|
|
|
79 |
match is a LIST containing type sre.SRE_Match
|
|
|
80 |
|
|
|
81 |
Returns a string (the filename) of the LIST match given as a parameter."""
|
|
|
82 |
fname = ''.join(match.string)
|
|
|
83 |
return fname
|
|
|
84 |
|
|
|
85 |
def getmatchnames(matches):
|
|
|
86 |
"""getmatchnames(matches):
|
|
|
87 |
|
|
|
88 |
matches is a String containing (many) objects of type sre.SRE_Match
|
|
|
89 |
|
|
|
90 |
Returns a LIST of STRINGS representing the filenames of the matches."""
|
|
|
91 |
matchnames = [fname_from_match(n) for n in matches]
|
|
|
92 |
return matchnames
|
|
|
93 |
|
|
|
94 |
def move_file(file, fromdir=WORK_DIR, todir=SORT_DIR):
|
|
|
95 |
"""move_file(file, dir):
|
|
|
96 |
|
|
|
97 |
file is a STRING representing a complete filename (with full path)
|
|
|
98 |
fromdir is a STRING representing where the file exists currently
|
|
|
99 |
destdir is a STRING representing the directory to move the file to
|
|
|
100 |
|
|
|
101 |
Returns either True if the move was successful, False otherwise."""
|
|
|
102 |
fromfile = os.path.join(fromdir, file)
|
|
|
103 |
destfile = os.path.join(todir, file)
|
|
|
104 |
|
|
|
105 |
try:
|
|
|
106 |
shutil.move(fromfile, destfile)
|
|
|
107 |
except:
|
|
|
108 |
return False
|
|
|
109 |
|
|
|
110 |
return True
|
|
|
111 |
|
|
|
112 |
def get_dstdir(dir):
|
|
|
113 |
"""get_dstdir(dir):
|
|
|
114 |
|
|
|
115 |
Get an appropriate destination directory depending on whether dir
|
|
|
116 |
has a slash on the beginning"""
|
|
|
117 |
if( dir[0] == '/' ):
|
|
|
118 |
return dir
|
|
|
119 |
|
|
|
120 |
return os.path.join(SORT_DIR, dir)
|
|
|
121 |
|
118 |
ira |
122 |
def printfailed(moved, failed, cwd):
|
|
|
123 |
"""printfailed(moved, failed, cwd):
|
116 |
ira |
124 |
|
|
|
125 |
moved is an INTEGER number representing the number of files successfully moved
|
118 |
ira |
126 |
failed is a LIST of the filenames which could not be moved
|
|
|
127 |
cwd is a STRING containing the current working directory"""
|
116 |
ira |
128 |
if( len(failed) > 0 and moved > 0 ):
|
|
|
129 |
print '================================================================================'
|
|
|
130 |
|
|
|
131 |
if( len(failed) > 0 and moved == 0 ):
|
118 |
ira |
132 |
printwdheader(cwd)
|
116 |
ira |
133 |
|
|
|
134 |
for f in failed:
|
|
|
135 |
print "Failed to move %s" % f
|
|
|
136 |
|
|
|
137 |
if( len(failed) == 0 and moved == 0 ):
|
|
|
138 |
pass
|
|
|
139 |
else:
|
|
|
140 |
print
|
|
|
141 |
|
|
|
142 |
def printwdheader(dir):
|
|
|
143 |
"""Prints out a header telling the user we're working in the directory
|
|
|
144 |
passed as an argument"""
|
|
|
145 |
print 'Working in directory: %s' % dir
|
|
|
146 |
print '================================================================================'
|
|
|
147 |
|
118 |
ira |
148 |
def printmove(num, file, dstdir, cwd):
|
116 |
ira |
149 |
if( num == 1 ):
|
118 |
ira |
150 |
printwdheader(cwd)
|
116 |
ira |
151 |
|
|
|
152 |
print "Moved %s to %s!" % (file, dstdir)
|
|
|
153 |
|
|
|
154 |
def mainloop(dir, dict):
|
|
|
155 |
"""mainloop(dir, dict):
|
|
|
156 |
|
|
|
157 |
dir is of type STRING
|
|
|
158 |
dict is of type LIST of TUPLES
|
|
|
159 |
|
|
|
160 |
Runs the main sort algorithm on the given directory using the pre-parsed
|
|
|
161 |
dictionary passed as a parameter."""
|
|
|
162 |
files = getfiles(dir)
|
|
|
163 |
subdirs = getdirs(dir)
|
|
|
164 |
cwd=dir
|
|
|
165 |
moved = 0
|
|
|
166 |
failed = []
|
|
|
167 |
|
|
|
168 |
for regex, todir in dict:
|
|
|
169 |
pattern = createpattern(regex)
|
|
|
170 |
matches = getmatches(files, pattern)
|
|
|
171 |
dstdir = get_dstdir(todir)
|
|
|
172 |
|
|
|
173 |
for f in matches:
|
|
|
174 |
ret = move_file(f, cwd, dstdir)
|
|
|
175 |
|
|
|
176 |
if( ret == False ):
|
|
|
177 |
failed.append(f)
|
|
|
178 |
else:
|
|
|
179 |
moved = moved + 1
|
118 |
ira |
180 |
printmove(moved, f, dstdir, cwd)
|
116 |
ira |
181 |
|
118 |
ira |
182 |
printfailed(moved, failed, cwd)
|
116 |
ira |
183 |
|
|
|
184 |
# Call ourself recursively on the subdirectories
|
|
|
185 |
for s in subdirs:
|
|
|
186 |
mainloop(s, dict)
|
|
|
187 |
|
|
|
188 |
##### THE MAIN PROGRAM STARTS HERE #####
|
|
|
189 |
if( __name__ == '__main__' ):
|
|
|
190 |
print 'Regular Expression File Sorter (aka animesorter)'
|
|
|
191 |
print '================================================================================'
|
|
|
192 |
print 'Copyright (c) 2005, Ira W. Snyder (devel@irasnyder.com)'
|
|
|
193 |
print 'All rights reserved.'
|
|
|
194 |
print 'This program is licensed under the GNU GPL v2'
|
|
|
195 |
print
|
|
|
196 |
|
|
|
197 |
dict = parsedict()
|
|
|
198 |
mainloop(WORK_DIR, dict)
|
|
|
199 |
|
|
|
200 |
print 'Goodbye!'
|