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 ###########################################################################
15 from functools
import wraps
16 from audiomangler
.config
import Config
17 from audiomangler
import util
18 from audiomangler
.codecs
import sync_sets
, get_codec
19 from audiomangler
.scanner
import scan
20 from audiomangler
.task
import PoolTask
21 from audiomangler
.logging
import *
23 def parse_options(options
= ()):
24 options
+= (('c:', '', '', 'set an arbitrary config option with an argument of option=value'),)
30 if len(sys
.argv
) == 1:
38 for (s_opt
, l_opt
, name
, desc
) in options
:
41 name_map
['-'+s_opt
.rstrip(':')] = name
45 name_map
['--'+l_opt
.rstrip('=')] = name
47 s_opts
= ''.join(s_opts
)
49 (opts
, args
) = getopt
.getopt(args
, s_opts
, l_opts
)
50 except getopt
.GetoptError
:
52 traceback
.print_exception(*sys
.exc_info())
57 k
, v
= v
.split('=', 1)
65 def print_usage(opts
):
67 %s [options] [files or directories to process]
69 options:""" % sys
.argv
[0]
70 for short
, long_
, name
, desc
in opts
:
72 print " -%s, --%-10s %s" % (short
.rstrip(':'), long_
.rstrip('='), desc
)
74 print " -%s %s" % (short
.rstrip(':'), desc
)
77 ('b:', 'base=', 'base', 'base directory for target files'),
78 ('p:', 'profile=', 'profile', 'profile to load settings from'),
79 ('f:', 'filename=', 'filename', 'format for target filenames'),
82 @parse_options(common_opts
)
84 dir_list
= scan(args
)[1]
85 util
.test_splits(dir_list
)
86 onsplit
= Config
['onsplit']
87 for (dir_
, files
) in dir_list
.items():
88 dir_p
= util
.fsdecode(dir_
)
89 msg(consoleformat
=u
"from dir %(dir_p)s:",
90 format
="enter: %(dir_)r", dir_
=dir_
, dir_p
=dir_p
, loglevel
=INFO
)
95 dst
= util
.fsencode(file_
.format())
96 src_p
= util
.fsdecode(src
)
97 dst_p
= util
.fsdecode(dst
)
99 msg(consoleformat
=u
" skipping %(src_p)s, already named correctly",
100 format
="skip: %(src)r",
101 src_p
=srcp_p
, src
=src
, loglevel
=INFO
)
103 dstdir
= os
.path
.split(dst
)[0]
104 if dstdir
not in dstdirs
and dstdir
!= dir_
:
108 if e
.errno
!= errno
.EEXIST
or not os
.path
.isdir(dstdir
):
111 msg(consoleformat
=u
" %(src_p)s -> %(dst_p)s",
112 format
="move: %(src)r, %(dst)r",
113 src
=src
, dst
=dst
, src_p
=src_p
, dst_p
=dst_p
, loglevel
=INFO
)
115 if len(dstdirs
) == 1:
116 dstdir
= dstdirs
.pop()
117 for file_
in os
.listdir(dir_
):
118 src
= os
.path
.join(dir_
, file_
)
119 dst
= os
.path
.join(dstdir
, file_
)
120 src_p
= util
.fsdecode(src
)
121 dst_p
= util
.fsdecode(dst
)
122 msg(consoleformat
=u
" %(src_p)s -> %(dst_p)s",
123 format
="move: %(src)r, %(dst)r", src
=src
, dst
=dst
,
124 src_p
=src_p
, dst_p
=dst_p
, loglevel
=INFO
)
126 while len(os
.listdir(dir_
)) == 0:
127 dir_p
= util
.fsdecode(dir_
)
128 msg(consoleformat
=u
" remove empty directory: %(dir_p)s",
129 format
="rmdir: %(dir_)r",
130 dir_
=dir_
, dir_p
=dir_p
, loglevel
=INFO
)
135 newdir
= os
.path
.split(dir_
)[0]
141 if onsplit
== 'warn':
142 msg(consoleformat
=u
"WARNING: tracks in %(dir_p)s were placed in different directories, other files may be left in the source directory",
143 format
="split: %(dir_)r",
144 dir_
=dir_
, dir_p
=dir_p
, loglevel
=WARNING
)
146 @parse_options(common_opts
+ (
147 ('t:', 'type=', 'type', 'type of audio to encode to'),
148 ('s:', 'preset=', 'preset', 'codec preset to use'),
149 ('e:', 'encopts=', 'encopts', 'encoder options to use'),
150 ('j:', 'jobs=', 'jobs', 'number of jobs to run'),
154 (album_list
, dir_list
) = scan(args
)[:2]
155 targettids
= scan(Config
['base'])[2]
156 sync_sets(album_list
.values(), targettids
)
158 def replaygain_task_generator(album_list
):
159 for key
, album
in album_list
.items():
163 getattr(track
, 'type_', None),
164 getattr(getattr(track
, 'info', None), 'sample_rate', None),
165 getattr(getattr(track
, 'info', None), 'channels', None)
167 if len(profiles
) != 1:
169 profile
= profiles
.pop()
170 if profile
[1] not in (8000, 11025, 12000, 16000, 22050, 24, 32, 44100, 48000):
171 msg(consoleformat
=u
"Skipping replaygain for %(albumtitle)s because of unsupported sample rate",
172 format
="skip rg (sample rate unsupported): %(tracks)r",
173 albumtitle
=album
[0].meta
.flat().get('album', '[unknown]'), tracks
=tuple(t
.filename
for t
in album
), loglevel
=VERBOSE
)
175 codec
= get_codec(profile
[0])
176 if not codec
or not codec
.has_replaygain
:
177 msg(consoleformat
=u
"Skipping replaygain for %(albumtitle)s because codec does not support it",
178 format
="skip rg (codec unsupported): %(tracks)r",
179 albumtitle
=album
[0].meta
.flat().get('album', '[unknown]'), tracks
=tuple(t
.filename
for t
in album
), loglevel
=VERBOSE
)
181 if reduce(lambda x
, y
: x
and y
.has_replaygain(), album
, True):
182 msg(consoleformat
=u
"Skipping replaygain for %(albumtitle)s because all tracks have replaygain tags",
183 format
="skip rg (already present): %(tracks)r",
184 albumtitle
=album
[0].meta
.flat().get('album', '[unknown]'), tracks
=tuple(t
.filename
for t
in album
), loglevel
=VERBOSE
)
186 msg(consoleformat
=u
"Adding replaygain values to %(albumtitle)s",
187 format
="rg: %(tracks)r",
188 albumtitle
=album
[0].meta
.flat().get('album', '[unknown]'), tracks
=tuple(t
.filename
for t
in album
))
189 yield codec
.add_replaygain([t
.filename
for t
in album
])
191 @parse_options(common_opts
[:2] + (
192 ('j:', 'jobs=', 'jobs', 'number of jobs to run'),
195 def replaygain(*args
):
197 args
= (Config
['base'], )
198 (album_list
) = scan(args
)[0]
199 PoolTask(replaygain_task_generator(album_list
)).run()