From 96d929f09aa28f1d5684743375721d0ac0e832e6 Mon Sep 17 00:00:00 2001 From: Mikhail Osipov Date: Fri, 1 Nov 2013 02:15:42 +0400 Subject: remove shntool usage external decoders support new options: --track-total, --track-start, --verbose --- cutter.py | 13 ++- cutter/cue.py | 5 +- cutter/formats/__base__.py | 28 ------- cutter/formats/__init__.py | 49 ++++++++--- cutter/formats/ape.py | 11 +++ cutter/formats/decoder.py | 160 +++++++++++++++++++++++++++++++++++ cutter/formats/encoder.py | 102 +++++++++++++++++++++++ cutter/formats/flac.py | 17 ++-- cutter/formats/handler.py | 12 +++ cutter/formats/mp3.py | 15 ++-- cutter/formats/ogg.py | 15 ++-- cutter/formats/sox.py | 23 +++++ cutter/formats/wav.py | 12 +-- cutter/formats/wavpack.py | 10 +++ cutter/splitter.py | 204 +++++++++++++++++++++++---------------------- cutter/tools.py | 7 ++ 16 files changed, 500 insertions(+), 183 deletions(-) delete mode 100644 cutter/formats/__base__.py create mode 100644 cutter/formats/ape.py create mode 100644 cutter/formats/decoder.py create mode 100644 cutter/formats/encoder.py create mode 100644 cutter/formats/handler.py create mode 100644 cutter/formats/sox.py create mode 100644 cutter/formats/wavpack.py diff --git a/cutter.py b/cutter.py index 9fa0cff..5ed49ab 100755 --- a/cutter.py +++ b/cutter.py @@ -19,13 +19,6 @@ 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)) @@ -75,6 +68,9 @@ def parse_args(): parser.add_option("-n", "--dry-run", action="store_true", default=False, dest="dry_run") + parser.add_option("-v", "--verbose", + dest="verbose", action="store_true", default=False) + enc = OptionGroup(parser, "Encoding options") enc.add_option("-t", "--type", dest="type", @@ -146,6 +142,9 @@ def parse_args(): else: tag.add_option("--" + opt, dest=opt, default="") + tag.add_option("--track-total", type="int", dest="tracktotal") + tag.add_option("--track-start", type="int", dest="trackstart") + parser.add_option_group(tag) return parser.parse_args() diff --git a/cutter/cue.py b/cutter/cue.py index a876eff..bfa6128 100644 --- a/cutter/cue.py +++ b/cutter/cue.py @@ -48,7 +48,10 @@ class File: return self.type in ("WAVE", "FLAC") def has_audio_tracks(self): - return len(list(self.tracks())) > 0 + return self.ntracks() > 0 + + def ntracks(self): + return len(list(self.tracks())) def split_points(self, info): rate = info.sample_rate * info.bits_per_sample * info.channels // 8 diff --git a/cutter/formats/__base__.py b/cutter/formats/__base__.py deleted file mode 100644 index f17748a..0000000 --- a/cutter/formats/__base__.py +++ /dev/null @@ -1,28 +0,0 @@ -class BaseHandler: - def __init__(self, logger = None): - self.logger = logger - self.buf = [] - - def log(self, fmt, *args): - if self.logger is not None: - self.logger(fmt, *args) - - def add(self, *args): - self.buf.extend(args) - - def build(self, join=True): - data = " ".join(self.buf) if join else self.buf - self.buf = [] - - return data - - def add_sox_args(self, opt, info): - if opt.sample_rate and opt.sample_rate != info.sample_rate: - self.add("-r %d" % opt.sample_rate) - if opt.bits_per_sample and opt.bits_per_sample != info.bits_per_sample: - self.add("-b %d" % opt.bits_per_sample) - if opt.channels and opt.channels != info.channels: - self.add("-c %d" % opt.channels) - - def is_tag_supported(self): - return hasattr(self, "tag") diff --git a/cutter/formats/__init__.py b/cutter/formats/__init__.py index 60a35fc..8d003f6 100644 --- a/cutter/formats/__init__.py +++ b/cutter/formats/__init__.py @@ -1,23 +1,50 @@ +from . handler import DecoderHandler, EncoderHandler + import os +handlers = ["flac", "ape", "ogg", "mp3", "wav", "wavpack"] + path = os.path.dirname(__file__) or "." -__formats = {} +__encoders = {} +__decoders = {} + +def __can_decode(obj): + return hasattr(obj, "decode") -for entry in sorted(os.listdir(path)): - if not entry.endswith(".py") or entry.startswith("_"): - continue +def __can_encode(obj): + return hasattr(obj, "encode") - modname = entry.replace(".py", "") - mod = __import__(modname, globals(), locals(), ["init"], 1) +for entry in handlers: + mod = __import__(entry, globals(), locals(), ["init"], 1) fmt = mod.init() - __formats[fmt.name] = fmt + + if __can_encode(fmt): + __encoders[fmt.name] = fmt + if __can_decode(fmt): + __decoders[fmt.ext] = fmt def supported(): - return sorted(__formats.keys()) + return sorted(__encoders.keys()) def issupported(name): - return name in __formats + return name in __encoders + +def encoder(name): + return EncoderHandler(__encoders.get(name)()) + +def decoder(name): + handler_type = __decoders.get(name) + + if not handler_type: + return None + + return DecoderHandler(handler_type()) + +def decoder_open(filename, *args, **kwargs): + ext = filename.rpartition(".")[-1] + handler = decoder(ext) + if handler is None: + return None -def handler(name, logger = None): - return __formats.get(name)(logger) + return handler.open(filename, *args, **kwargs) diff --git a/cutter/formats/ape.py b/cutter/formats/ape.py new file mode 100644 index 0000000..8d5b1b7 --- /dev/null +++ b/cutter/formats/ape.py @@ -0,0 +1,11 @@ +class ApeHandler: + name = "ape" + ext = "ape" + cmd = "mac" + + def decode(self, filename): + args = [self.cmd, filename, "-", "-d"] + return args + +def init(): + return ApeHandler diff --git a/cutter/formats/decoder.py b/cutter/formats/decoder.py new file mode 100644 index 0000000..99f2cbe --- /dev/null +++ b/cutter/formats/decoder.py @@ -0,0 +1,160 @@ +from . handler import * +from .. tools import quote +from .. coding import to_unicode + +import subprocess +import wave + +class DecoderError(Exception): + pass + +class StreamInfo: + pass + +class Decoder: + class Reader: + def __init__(self, stream, nframes): + self.stream = stream + self.nframes = nframes + self.bytes_per_frame = stream._channels * stream._bytes_per_sample + self.nread = 0 + + def info(self): + return self.stream.info() + + def wave_params(self): + params = list(self.stream.reader.getparams()) + params[3] = self.nframes + return params + + def read(self, maxframes=None): + if self.nread >= self.nframes: + return [] + + avail = self.nframes - self.nread + if maxframes and maxframes < avail: + avail = maxframes + + data = self.stream.reader.readframes(avail) + count = len(data) // self.bytes_per_frame + self.nread += count + + return data + + def __init__(self, handler, filename, options=None): + self.reader = None + self.closed = False + self.handler = handler + + args = self.handler.decode(filename) + self.command = " ".join(map(quote, args)) + + self.proc = subprocess.Popen( + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + try: + self.reader = wave.open(self.proc.stdout, "r") + except Exception as exc: + self.close() + if self.status: + self.status_msg = to_unicode(self.proc.stderr.read()) + else: + self.status_msg = "wave.open: %s" % exc + return + + self._channels = self.reader.getnchannels() + self._bytes_per_sample = self.reader.getsampwidth() + self._sample_rate = self.reader.getframerate() + + def ready(self): + return self.reader is not None + + def channels(self): + return self._channels + + def bits_per_sample(self): + return self._bytes_per_sample * 8 + + def sample_rate(self): + return self._sample_rate + + def info(self): + info = StreamInfo() + + info.channels = self._channels + info.bits_per_sample = self.bits_per_sample() + info.sample_rate = self._sample_rate + info.type = self.handler.name + + return info + + def seek(self, pos): + nframes = pos * self._sample_rate // 75 - self.reader.tell() + + if nframes: + r = self.Reader(self, nframes) + while len(r.read()): + pass + + return nframes + + def get_reader(self, npsecs): + if npsecs is None: + nframes = self.reader.getnframes() - self.reader.tell() + else: + nframes = npsecs * self._sample_rate // 75 + + return self.Reader(self, nframes) + + def get_command(self): + return self.command + + def get_status(self): + return self.status, self.status_msg + + def close(self): + if self.closed: + return + + if self.reader: + self.reader.close() + + if self.proc: + self.proc.stdout.close() + self.status = self.proc.wait() + else: + self.status = 0 + + self.status_msg = "" + self.closed = True + + def __del__(self): + self.close() + +class DummyDecoder(Decoder): + class DummyReader(Decoder.Reader): + def __init__(self, *args): + Decoder.Reader.__init__(self, *args) + + def info(self): + return self.stream.info() + + def read(self, *args): + return [] + + def __init__(self, *args, **kwargs): + Decoder.__init__(self, *args, **kwargs) + + def seek(self, *args): + pass + + def get_reader(self, *args): + return self.DummyReader(self, *args) + +class DecoderHandler(Handler): + def open(self, filename, options=None): + if options and options.dry_run: + return DummyDecoder(self.handler, filename, options) + else: + return Decoder(self.handler, filename, options) diff --git a/cutter/formats/encoder.py b/cutter/formats/encoder.py new file mode 100644 index 0000000..23d0c51 --- /dev/null +++ b/cutter/formats/encoder.py @@ -0,0 +1,102 @@ +from . handler import * +from .. tools import quote + +import subprocess +import wave + +class EncoderError(Exception): + pass + +class Encoder: + FRAME_BUFFER_SIZE=0x4000 + + class SafeStream: + MIN_CHUNK_SIZE=64 + + def __init__(self, fileobj): + self.fileobj = fileobj + self.seek_called = False + self.written = 0 + + self.buffered = b"" + + def seek(self, *args): + self.seek_called = True + + def write(self, data): + if self.seek_called: + return + + nbytes = len(data) + if nbytes < self.MIN_CHUNK_SIZE: + self.buffered += data + else: + if len(self.buffered): + data = self.buffered + data + self.buffered = b"" + + self.fileobj.write(data) + + self.written += nbytes + + def tell(self): + return self.written + + def close(self): + if len(self.buffered): + self.fileobj.write(self.buffered) + + self.fileobj.close() + + def __getattr__(self, attr): + return getattr(self.fileobj, attr) + + def __init__(self, handler, reader, filename, options): + self.closed = False + self.handler = handler + + args = self.handler.encode(filename, options, reader.info()) + self.command = " ".join(map(quote, args)) + + if options.dry_run: + self.proc = None + return + + self.proc = subprocess.Popen(args, stdin=subprocess.PIPE) + + self.reader = reader + self.stream = self.SafeStream(self.proc.stdin) + self.writer = wave.open(self.stream, "w") + self.writer.setparams(reader.wave_params()) + + def process(self): + if self.proc is None: + return + + while True: + data = self.reader.read(self.FRAME_BUFFER_SIZE) + if not len(data): + break + + self.writer.writeframesraw(data) + + def get_command(self): + return self.command + + def close(self): + if self.closed or self.proc is None: + return + + self.writer.close() + self.stream.close() + self.proc.wait() + + def __del__(self): + self.close() + +class EncoderHandler(Handler): + def open(self, reader, filename, options): + return Encoder(self.handler, reader, filename, options) + + def is_tag_supported(self): + return hasattr(self.handler, "tag") diff --git a/cutter/formats/flac.py b/cutter/formats/flac.py index 2723344..6f47d94 100644 --- a/cutter/formats/flac.py +++ b/cutter/formats/flac.py @@ -1,22 +1,21 @@ -from . __base__ import * +from . sox import * from .. coding import to_bytes import subprocess -class FlacHandler(BaseHandler): +class FlacHandler(SoxHandler): name = "flac" ext = "flac" + cmd = "flac" - def encode(self, opt, info): - self.add("flac sox -") - + def encode(self, path, opt, info): if opt.compression is not None: - self.add("-C %d" % opt.compression) + self.set_compression(opt.compression) - self.add_sox_args(opt, info) - self.add("%f") + return self.sox_args(path, opt, info) - return self.build() + def decode(self, filename): + return [self.cmd, "-d", "-c", "-s", filename] def tag(self, path, tags): args = ["metaflac", "--remove-all-tags", "--import-tags-from=-", path] diff --git a/cutter/formats/handler.py b/cutter/formats/handler.py new file mode 100644 index 0000000..9a322c1 --- /dev/null +++ b/cutter/formats/handler.py @@ -0,0 +1,12 @@ +def dev_null(): + return open("/dev/null") + +class Handler: + def __init__(self, handler): + self.handler = handler + + def __getattr__(self, attr): + return getattr(self.handler, attr) + +from . encoder import EncoderHandler +from . decoder import DecoderHandler diff --git a/cutter/formats/mp3.py b/cutter/formats/mp3.py index ecccbb4..ba78b85 100644 --- a/cutter/formats/mp3.py +++ b/cutter/formats/mp3.py @@ -1,4 +1,4 @@ -from . __base__ import * +from . sox import * from .. coding import to_bytes import subprocess @@ -84,20 +84,15 @@ class ID3Tagger: fp.close() -class Mp3Handler(BaseHandler): +class Mp3Handler(SoxHandler): name = "mp3" ext = "mp3" - def encode(self, opt, info): - self.add("cust ext=%s sox -" % self.ext) - + def encode(self, path, opt, info): if opt.bitrate is not None: - self.add("-C %d" % opt.bitrate) - - self.add_sox_args(opt, info) - self.add("%f") + self.set_compression(opt.bitrate) - return self.build() + return self.sox_args(path, opt, info) def tag(self, path, tags): tagger = ID3Tagger() diff --git a/cutter/formats/ogg.py b/cutter/formats/ogg.py index c881058..dc6f9f3 100644 --- a/cutter/formats/ogg.py +++ b/cutter/formats/ogg.py @@ -1,22 +1,17 @@ -from . __base__ import * +from . sox import * from .. coding import to_bytes import subprocess -class OggHandler(BaseHandler): +class OggHandler(SoxHandler): name = "ogg" ext = "ogg" - def encode(self, opt, info): - self.add("cust ext=%s sox -" % self.ext) - + def encode(self, path, opt, info): if opt.compression is not None: - self.add("-C %d" % opt.compression) - - self.add_sox_args(opt, info) - self.add("%f") + self.set_compression(opt.compression) - return self.build() + return self.sox_args(path, opt, info) def tag(self, path, tags): args = ["vorbiscomment", "--raw", "--write", path] diff --git a/cutter/formats/sox.py b/cutter/formats/sox.py new file mode 100644 index 0000000..2a28a24 --- /dev/null +++ b/cutter/formats/sox.py @@ -0,0 +1,23 @@ +class SoxHandler: + def __init__(self): + self.compression = None + + def set_compression(self, value): + self.compression = value + + def sox_args(self, path, opt, info): + args = ["sox", "-V2", "-"] + + if self.compression is not None: + args.extend(["-C", str(self.compression)]) + + if opt.sample_rate and opt.sample_rate != info.sample_rate: + args.extend(["-r", str(opt.sample_rate)]) + if opt.bits_per_sample and opt.bits_per_sample != info.bits_per_sample: + args.extend(["-b", str(opt.bits_per_sample)]) + if opt.channels and opt.channels != info.channels: + args.extend(["-c", str(opt.channels)]) + + args.append(path) + + return args diff --git a/cutter/formats/wav.py b/cutter/formats/wav.py index 131ea73..41e18d9 100644 --- a/cutter/formats/wav.py +++ b/cutter/formats/wav.py @@ -1,15 +1,11 @@ -from . __base__ import * +from . sox import * -class WavHandler(BaseHandler): +class WavHandler(SoxHandler): name = "wav" ext = "wav" - def encode(self, opt, info): - self.add("wav sox -") - self.add_sox_args(opt, info) - self.add("%f") - - return self.build() + def encode(self, path, opt, info): + return self.sox_args(path, opt, info) def init(): return WavHandler diff --git a/cutter/formats/wavpack.py b/cutter/formats/wavpack.py new file mode 100644 index 0000000..d575481 --- /dev/null +++ b/cutter/formats/wavpack.py @@ -0,0 +1,10 @@ +class WavpackHandler: + name = "wavpack" + ext = "wv" + cmd = "wvunpack" + + def decode(self, filename): + return [self.cmd, "-q", filename, "-o", "-"] + +def init(): + return WavpackHandler diff --git a/cutter/splitter.py b/cutter/splitter.py index 45cd0be..24e8eac 100644 --- a/cutter/splitter.py +++ b/cutter/splitter.py @@ -65,37 +65,22 @@ class TempLink: self.remove() class StreamInfo: - __mapping = { - b"Channels:": "channels", - b"Bits/sample:": "bits_per_sample", - b"Samples/sec:": "sample_rate" - } - @staticmethod def get(name): - info = StreamInfo() - proc = subprocess.Popen(["shninfo", name], stdout = subprocess.PIPE) - for line in proc.stdout.readlines(): - data = line.split() - attr = StreamInfo.__mapping.get(data[0]) - if attr: - setattr(info, attr, int(data[1])) - elif line.startswith(b"Handled by:"): - info.type = to_unicode(data[2]) - - if proc.wait(): + stream = formats.decoder_open(name) + + if not stream or not stream.ready(): return None - return info + return stream.info() class Splitter: EXT = ["ape", "flac", "wv"] class File: - def __init__(self, fileobj, name, info): + def __init__(self, fileobj, path): self.fileobj = fileobj - self.name = name - self.info = info + self.path = path def __getattr__(self, attr): return getattr(self.fileobj, attr) @@ -125,6 +110,8 @@ class Splitter: sys.exit(1) def init_tags(self): + self.tracktotal = self.opt.tracktotal or len(list(self.all_tracks())) + self.tags = { "album": self.opt.album or self.cue.get("title"), "date": self.opt.date or self.cue.get("date"), @@ -145,21 +132,20 @@ class Splitter: self.dest = os.path.join(self.opt.dir, tmp) track_fmt = os.path.basename(self.opt.fmt) - tracknumber = 0 + tracknumber = self.opt.trackstart or 1 self.track_info = {} for track in self.all_tracks(): - tracknumber += 1 self.track_info[track] = self.get_track_info( track, tracknumber, track_fmt ) + tracknumber += 1 def __init__(self, cue, opt): self.cue = cue self.opt = opt - self.tracktotal = len(list(self.all_tracks())) - self.enctype = formats.handler(opt.type, logger=printf) - self.tag_supported = self.enctype.is_tag_supported() + self.encoder = formats.encoder(opt.type) + self.tag_supported = self.encoder.is_tag_supported() self.init_tags() @@ -181,7 +167,7 @@ class Splitter: if self.opt.convert_chars: name = convert_characters(name) - return self.TrackInfo(name + "." + self.enctype.ext, tags) + return self.TrackInfo(name + "." + self.encoder.ext, tags) def find_realfile(self, name): if not name.endswith(".wav"): @@ -203,25 +189,20 @@ class Splitter: debug("skip file %s: no tracks", quote(file.name)) continue - name = self.cue.dir + file.name - if not os.path.exists(name): + path = self.cue.dir + file.name + if not os.path.exists(path): real = self.find_realfile(file.name) if not real: printerr("no such file %s", quote(file.name)) sys.exit(1) - name = self.cue.dir + real + path = self.cue.dir + real - info = StreamInfo.get(name) - if info is None: - printerr("%s: unknown type", quote(file.name)) - sys.exit(1) - - lst.append(self.File(file, name, info)) + lst.append(self.File(file, path)) return lst def shntool_args(self, tool, info): - encode = self.enctype.encode(self.opt, info) + encode = self.encoder.encode(self.opt, info) return [tool, "-w", "-d", self.dest, "-o", encode] def track_name(self, track): @@ -234,37 +215,41 @@ class Splitter: if not self.tag_supported: return - printf("Tag [%s] : ", path) - if not self.enctype.tag(path, self.track_tags(track)): + printf("tag %s: ", quote(self.track_name(track))) + if not self.encoder.tag(path, self.track_tags(track)): printf("FAILED\n") sys.exit(1) printf("OK\n") - def copy_file(self, file): + def is_need_convert(self, info): noteq = lambda a, b: a and a != b - if file.info.type != self.enctype.name: - return False - if noteq(self.opt.sample_rate, file.info.sample_rate): - return False - if noteq(self.opt.bits_per_sample, file.info.bits_per_sample): - return False - if noteq(self.opt.channels, file.info.channels): - return False + if info.type != self.encoder.name: + return True + if noteq(self.opt.sample_rate, info.sample_rate): + return True + if noteq(self.opt.bits_per_sample, info.bits_per_sample): + return True + if noteq(self.opt.channels, info.channels): + return True + + return False + def copy_file(self, file): track = list(file.tracks())[0] trackname = self.track_name(track) path = os.path.join(self.dest, trackname) + printf("copy %s -> %s", quote(file.path), quote(path)) if self.opt.dry_run: - printf("Copy [%s] --> [%s]\n", file.name, path) - return True + printf("\n") + return - printf("Copy [%s] --> [%s] : ", file.name, path) + printf(": ") try: - shutil.copyfile(file.name, path) + shutil.copyfile(file.path, path) except Exception as err: printf("FAILED: %s\n", err) sys.exit(1) @@ -273,65 +258,91 @@ class Splitter: self.tag(track, path) - return True + def open_decode(self, path): + stream = formats.decoder_open(path, self.opt) - def convert_file(self, file): - track = list(file.tracks())[0] - trackname = self.track_name(track) + if stream is None: + printerr("%s: unsupported type", quote(path)) + elif not stream.ready(): + printerr("decode failed, command: %s", stream.get_command()) - args = self.shntool_args("shnconv", file.info) + status, msg = stream.get_status() + if not len(msg): + printerr("exit code %d", status) + else: + printerr("exit code %d, stderr:\n%s", status, msg) - if self.opt.dry_run: - name = "link to " + quote(file.name, "'") - debug("run %s", " ".join(map(quote, args + [name]))) - return + stream = None - try: - link = TempLink(os.path.abspath(file.name), trackname) - except OSError as err: - printerr("create symlink %s failed: %s", quote(trackname), err) - sys.exit(1) + return stream - ret = subprocess.call(args + [str(link)]) - link.remove() + def print_decode_info(self, path, stream): + info = stream.info() + debug("decode: %s", stream.get_command()) + debug("input: %s [%s] (%d/%d, %d ch)", quote(path), + info.type, info.bits_per_sample, info.sample_rate, + info.channels) - if ret: - printerr("shnconv failed: exit code %d", ret); - sys.exit(1) + def print_encode_info(self, stream): + debug("encode: %s", stream.get_command()) + + @staticmethod + def track_timerange(track): + ts = "%8s -" % msf(track.begin) - self.tag(track, os.path.join(self.dest, trackname)) + if track.end is not None: + ts += " %8s" % msf(track.end) - def split_file(self, file, points): - args = self.shntool_args("shnsplit", file.info) + [file.name] + return ts - if self.opt.dry_run: - debug("run %s", " ".join(map(quote, args))) + @staticmethod + def track_length(track): + if track.end is None: + return None + + return track.end - track.begin + + def split_file(self, file): + stream = self.open_decode(file.path) + if not stream: return - proc = subprocess.Popen(args, stdin = subprocess.PIPE) - proc.stdin.write(to_bytes("\n".join(map(str, points)))) - proc.stdin.close() + if self.opt.verbose and self.opt.dry_run: + self.print_decode_info(file.path, stream) - if proc.wait(): - printerr("shnsplit failed: exit code %d", proc.returncode) - sys.exit(1) + if file.ntracks() == 1: + if not self.is_need_convert(stream.info()): + stream.close() + self.copy_file(file) + return - splitted = filterdir(self.dest, "split-track") - for track, filename in zip(file.tracks(), splitted): + for track in file.tracks(): trackname = self.track_name(track) path = os.path.join(self.dest, trackname) - printf("Rename [%s] --> [%s] : ", filename, trackname) - try: - os.rename(os.path.join(self.dest, filename), path) - except OSError as err: - printf("FAILED: %s\n", err) - sys.exit(1) - else: - printf("OK\n") + ts = self.track_timerange(track) + printf("split %s (%s) -> %s", quote(file.path), ts, quote(trackname)) + printf("\n" if self.opt.dry_run else ": ") + + stream.seek(track.begin) + reader = stream.get_reader(self.track_length(track)) + + out = self.encoder.open(reader, path, self.opt) + + if self.opt.dry_run: + if self.opt.verbose: + self.print_encode_info(out) + continue + + out.process() + out.close() + + printf("OK\n") self.tag(track, path) + stream.close() + def check_duplicates(self): names = [x.name for x in self.track_info.values()] dup = [k for k, v in collections.Counter(names).items() if v > 1] @@ -346,7 +357,7 @@ class Splitter: debug("skip non file %s", quote(file)) continue - printf("Copy [%s] into [%s] : ", file, dest) + printf("copy %s -> %s: ", quote(file), quote(dest)) try: shutil.copy(path, dest) @@ -370,12 +381,7 @@ class Splitter: self.dest = to_unicode(tempdir) for file in files: - points = list(file.split_points(file.info)) - if not points: - if not self.copy_file(file): - self.convert_file(file) - else: - self.split_file(file, points) + self.split_file(file) if self.realpath: self.transfer_files(self.dest, self.realpath) diff --git a/cutter/tools.py b/cutter/tools.py index 69d8bdb..8afbaa4 100644 --- a/cutter/tools.py +++ b/cutter/tools.py @@ -24,3 +24,10 @@ def debug(fmt, *args): if msg[-1] != "\n": msg += "\n" sys.stderr.write("-- " + msg) + +def msf(ts): + m = ts / (60 * 75) + s = ts / 75 % 60 + f = ts % 75 + + return "%d:%02d.%02d" % (m, s, f) -- cgit v1.2.3-70-g09d2