diff options
| author | mikeos <mike.osipov@gmail.com> | 2013-09-30 22:11:41 +0400 |
|---|---|---|
| committer | mikeos <mike.osipov@gmail.com> | 2013-09-30 22:23:41 +0400 |
| commit | 6f6c2754c4b73946eab3faa4dc7e9e672eb373b8 (patch) | |
| tree | 3daa8109c4c0d5978a1aa83112b4dafdebe52cde /cutter.py | |
| parent | 0ae5a430e4f92ee7bf9e458cf584a0a12fd5c25a (diff) | |
setup.py
Diffstat (limited to 'cutter.py')
| -rwxr-xr-x | cutter.py | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/cutter.py b/cutter.py new file mode 100755 index 0000000..8ffeba6 --- /dev/null +++ b/cutter.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python + +from cutter import formats, cue +from cutter.coding import to_unicode, to_bytes +from cutter.splitter import Splitter +from cutter.tools import * + +from optparse import OptionParser, OptionGroup + +import sys +import os + +try: + from cutter import config +except Exception as err: + printerr("import config failed: %s", err) + sys.exit(0) + +def msf(ts): + m = ts / (60 * 75) + s = ts / 75 % 60 + f = ts % 75 + + return "%d:%02d:%02d" % (m, s, f) + +def print_cue(cue): + for k, v in cue.attrs(): + printf("%s: %s\n", k.upper(), quote(v)) + + for file in cue.files(): + name = cue.dir + file.name + + printf("FILE %s", quote(file.name)) + if not os.path.exists(name): + printf(": not exists\n") + else: + info = StreamInfo.get(name) + if not info: + printf(": unknown type\n") + else: + printf(" [%s] (%d/%d, %d ch)\n", + info.type, + info.bits_per_sample, + info.sample_rate, + info.channels) + + for track in file.tracks(): + printf("\tTRACK %02d", track.number) + title = track.get("title") + if title != "": + printf(" %s", quote(title)) + printf(": %s -", msf(track.begin)) + if track.end is not None: + printf(" %s", msf(track.end)) + printf("\n") + + for k, v in track.attrs(): + if k not in ("pregap", "postgap", "title"): + printf("\t\t%s: %s\n", k.upper(), quote(v)) + +def parse_args(): + parser = OptionParser(usage = u"Usage: %prog [options] cuefile") + parser.add_option("--ignore", + action="store_true", default=False, dest="ignore", + help="ignore cue parsing errors") + + parser.add_option("--dump", + dest="dump", choices=["cue", "tags", "tracks"], + metavar="cue|tags|tracks", + help="print the cue sheet, file tags or track names") + + parser.add_option("-n", "--dry-run", + action="store_true", default=False, dest="dry_run") + + enc = OptionGroup(parser, "Encoding options") + + enc.add_option("-t", "--type", dest="type", + choices = formats.supported() + ["help"], + help="output file format") + + enc.add_option("--coding", dest="coding", + help="encoding of original text") + + enc.add_option("-d", "--dir", + dest="dir", default=config.DIR, help="output directory") + + enc.add_option("--use-tempdir", + dest="use_tempdir", action="store_true", + help="use temporary directory for files") + + enc.add_option("--no-tempdir", + dest="use_tempdir", action="store_false", + help="do not use temporary directory") + + enc.add_option("-C", "--compression", type="int", + dest="compression", metavar="FACTOR", + help="compression factor for output format (used for flac, ogg)") + + enc.add_option("--bitrate", type="int", + dest="bitrate", default=config.MP3_BITRATE, + help="audio bitrate (used for mp3)") + + parser.add_option_group(enc) + + fname = OptionGroup(parser, "Filename options") + + fname.add_option("--format", + dest="fmt", default=config.FILENAME_FORMAT, + help="the format string for new filenames") + + fname.add_option("--convert-chars", + dest="convert_chars", action="store_true", + help="replace illegal characters in filename") + + fname.add_option("--no-convert-chars", + dest="convert_chars", action="store_false", + help="do not replace characters in filename") + + parser.add_option_group(fname) + + format = OptionGroup(parser, "Output format") + + format.add_option("-r", "--sample-rate", type="int", + dest="sample_rate", default=config.SAMPLE_RATE, metavar="RATE") + + format.add_option("-c", "--channels", type="int", + dest="channels", default=config.CHANNELS) + + format.add_option("-b", "--bits-per-sample", type="int", + dest="bits_per_sample", default=config.BITS_PER_SAMPLE, metavar="BITS") + + parser.add_option_group(format) + + tag = OptionGroup(parser, "Tag options") + tag_options = ["album", "artist", ("date", "year"), "genre", + "comment", "composer", "albumartist"] + + for opt in tag_options: + if type(opt) in (list, tuple): + tag.add_option(*["--" + s for s in opt], dest=opt[0], default="") + else: + tag.add_option("--" + opt, dest=opt, default="") + + parser.add_option_group(tag) + + return parser.parse_args() + +def option_check_range(option, value, min, max): + if value is not None and (value < min or value > max): + printerr("invalid %s value %d, must be in range %d .. %d", option, value, min, max) + return False + + return True + +def process_options(opt): + def choose(a, b): + return a if a is not None else b + + if opt.type == "help": + printerr("supported formats: " + " ".join(formats.supported())) + return False + + if opt.type is None and config.TYPE: + if not formats.issupported(config.TYPE): + printerr("invalid configuration: type '%s' is not supported", config.TYPE) + return False + + opt.type = config.TYPE + + if not opt.dump and opt.type is None: + printerr("--type option is missed") + return False + + if opt.type == "flac": + opt.compression = choose(opt.compression, config.FLAC_COMPRESSION) + if not option_check_range("compression", opt.compression, 0, 8): + return False + elif opt.type == "ogg": + opt.compression = choose(opt.compression, config.OGG_COMPRESSION) + if not option_check_range("compression", opt.compression, -1, 10): + return False + elif opt.type == "mp3": + if not option_check_range("bitrate", opt.bitrate, 32, 320): + return False + + if not opt.dir: + opt.dir = u"." + else: + opt.dir = to_unicode(os.path.normpath(opt.dir)) + + opt.fmt = to_unicode(opt.fmt) + if not os.path.basename(opt.fmt): + printerr("invalid format option \"%s\"", opt.fmt) + return False + else: + opt.fmt = os.path.normpath(opt.fmt) + if opt.fmt.startswith("/"): + opt.fmt = opt.fmt[1:] + + if opt.convert_chars is None: + opt.convert_chars = config.CONVERT_CHARS + if opt.use_tempdir is None: + opt.use_tempdir = config.USE_TEMPDIR + + return True + +def find_cuefile(path): + for file in os.listdir(path): + fullname = os.path.join(path, file) + if os.path.isfile(fullname) and file.endswith(".cue"): + return fullname + + printerr("no cue file") + sys.exit(1) + +def main(): + options, args = parse_args() + if not process_options(options): + sys.exit(1) + + if len(args) != 1: + printf("Usage: %s [options] cuefile\n", progname) + return 1 + + def on_error(err): + printerr("%d: %s\n" % (err.line, err)) + if not options.ignore: + raise StopIteration + + cuepath = to_unicode(args[0]) + if os.path.isdir(cuepath): + cuepath = find_cuefile(cuepath) + if options.dry_run: + debug("use cue file %s", quote(cuepath)) + + try: + cuesheet = cue.read(cuepath, options.coding, on_error=on_error) + except StopIteration: + return 1 + except IOError as err: + printerr("open %s: %s", err.filename, err.strerror) + return 1 + except Exception as err: + msg = "%s (%s)" % (err, err.__class__.__name__) + + if hasattr(err, "filename"): + printerr("%s: %s: %s\n", err.filename, msg) + else: + printerr("%s\n", msg) + + return 1 + + cuesheet.dir = os.path.dirname(cuepath) + if cuesheet.dir: + cuesheet.dir += "/" + + { + "cue": lambda: print_cue(cue), + "tags": lambda: Splitter(cuesheet, options).dump_tags(), + "tracks": lambda: Splitter(cuesheet, options).dump_tracks(), + None: lambda: Splitter(cuesheet, options).split() + }[options.dump]() + + return 0 + +if __name__ == '__main__': + sys.exit(main()) |
