add logging module, use logging in cli.rename, cli uses decorator to parse options...
[audiomangler.git] / audiomangler / cli.py
blob8bd74b2012c6be6fc27abf28af0eed5a6a6ba277
1 # -*- coding: utf-8 -*-
2 ###########################################################################
3 # Copyright (C) 2008 by Andrew Mahone
4 # <andrew.mahone@gmail.com>
6 # Copyright: See COPYING file that comes with this distribution
8 ###########################################################################
9 import sys
10 import getopt
11 import shutil
12 import os
13 import os.path
14 import errno
15 from functools import wraps
16 from audiomangler import scan, Config, util, sync_sets, get_codec, err, msg, fatal, ERROR, WARNING, INFO, DEBUG
18 def parse_options(options = []):
19 def decorator(f):
20 @wraps(f)
21 def proxy(*args):
22 if not args:
23 if len(sys.argv) == 1:
24 print_usage(options)
25 sys.exit(0)
26 else:
27 args = sys.argv[1:]
28 name_map = {}
29 s_opts = []
30 l_opts = []
31 for (s_opt, l_opt, name, desc) in options:
32 if s_opt:
33 name_map['-'+s_opt.rstrip(':')] = name
34 s_opts.append(s_opt)
35 if l_opt:
36 name_map['--'+l_opt.rstrip('=')] = name
37 l_opts.append(l_opt)
38 s_opts = ''.join(s_opts)
39 try:
40 (opts, args) = getopt.getopt(args,s_opts,l_opts)
41 except getopt.GetoptError:
42 print_usage(options)
43 sys.exit(0)
44 for k,v in opts:
45 k = name_map[k]
46 Config[k] = v
47 f(*args)
48 return proxy
49 return decorator
51 def print_usage(opts):
52 print """usage:
53 %s [options] [files or directories to process]
55 options:""" % sys.argv[0]
56 for short,long_,name,desc in opts:
57 print " -%s, --%-10s %s" %(short.rstrip(':'),long_.rstrip('='),desc)
59 common_opts = (
60 ('b:','base=','base','base directory for target files'),
61 ('p:','profile=','profile','profile to load settings from'),
62 ('f:','filename=','filename','format for target filenames'),
65 @parse_options(common_opts)
66 def rename(*args):
67 dir_list = scan(args)[1]
68 util.test_splits(dir_list)
69 onsplit = Config['onsplit']
70 for (dir_,files) in dir_list.items():
71 dir_p = util.fsdecode(dir_)
72 msg(consoleformat=u"from dir %(dir_p)s:",
73 format="enter: %(dir_)r", dir_=dir_, dir_p=dir_p, loglevel=INFO)
74 dstdirs = set()
75 moves = []
76 for file_ in files:
77 src = file_.filename
78 dst = util.fsencode(file_.format())
79 src_p = util.fsdecode(src)
80 dst_p = util.fsdecode(dst)
81 if src == dst:
82 msg(consoleformat=u" skipping %(src_p)s, already named correctly",
83 format="skip: %(src)r", src_p=srcp_p, src=src, loglevel=INFO)
84 continue
85 dstdir = os.path.split(dst)[0]
86 if dstdir not in dstdirs and dstdir != dir_:
87 try:
88 os.makedirs(dstdir)
89 except OSError, e:
90 if e.errno != errno.EEXIST or not os.path.isdir(dstdir):
91 raise
92 dstdirs.add(dstdir)
93 msg(consoleformat=u" %(src_p)s -> %(dst_p)s",
94 format="move: %(src)r, %(dst)r", src=src, dst=dst, src_p=src_p, dst_p=dst_p, loglevel=INFO)
95 util.move(src,dst)
96 if len(dstdirs) == 1:
97 dstdir = dstdirs.pop()
98 for file_ in os.listdir(dir_):
99 src = os.path.join(dir_,file_)
100 dst = os.path.join(dstdir,file_)
101 src_p = util.fsdecode(src)
102 dst_p = util.fsdecode(dst)
103 msg(consoleformat=u" %(src_p)s -> %(dst_p)s",
104 format="move: %(src)r, %(dst)r", src=src, dst=dst, src_p=src_p, dst_p=dst_p, loglevel=INFO)
105 util.move(src,dst)
106 while len(os.listdir(dir_)) == 0:
107 dir_p = util.fsdecode(dir_)
108 msg(consoleformat=u" remove empty directory: %(dir_p)s",
109 format="rmdir: %(dir_)r", dir_=dir_, dir_p=dir_p, loglevel=INFO)
110 try:
111 os.rmdir(dir_)
112 except Exception:
113 break
114 newdir = os.path.split(dir_)[0]
115 if newdir != dir_:
116 dir_ = newdir
117 else:
118 break
119 else:
120 if onsplit == 'warn':
121 msg(consoleformat=u"WARNING: tracks in %(dir_p)s were placed in different directories, other files may be left in the source directory",
122 format="split: %(dir_)r", dir_=dir_, dir_p=dir_p, loglevel=WARNING)
124 @parse_options(common_opts + (
125 ('t:','type=','type','type of audio to encode to'),
126 ('s:','preset=','preset','codec preset to use'),
127 ('e:','encopts=','encopts','encoder options to use'),
128 ('j:','jobs=','jobs','number of jobs to run'),
131 def sync(*args):
132 (album_list, dir_list) = scan(args)[:2]
133 targettids = scan(Config['base'])[2]
134 sync_sets(album_list.values(),targettids)
136 @parse_options(common_opts[:2])
137 def replaygain(args = None):
138 args = parse_options(args, sync_opts)
139 if not args:
140 args = (Config['base'],)
141 (album_list) = scan(args)[0]
142 for album in album_list.values():
143 profiles = set()
144 for track in album:
145 profiles.add((
146 getattr(track,'type_',None),
147 getattr(getattr(track,'info',None),'sample_rate',None),
148 getattr(getattr(track,'info',None),'channels',None)
150 if len(profiles) != 1:
151 continue
152 profile = profiles.pop()
153 if profile[1] not in (8000,11025,12000,16000,22050,24,32,44100,48000):
154 continue
155 codec = get_codec(profile[0])
156 if not codec or not codec._replaygain:
157 continue
158 if reduce(lambda x,y: x and y.has_replaygain(), album, True):
159 continue
160 codec.add_replaygain([t.filename for t in album])
162 __all__ = []