From 7410948eb8a5fcf386d66c4ac3d60bd935255606 Mon Sep 17 00:00:00 2001 From: mikeos Date: Mon, 22 Jul 2013 23:32:55 +0400 Subject: new tag options composer and albumartist, extended dump option --- cue.py | 18 ++++++++------ cuedump.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ cueread.py | 64 ------------------------------------------------ cutter | 82 ++++++++++++++++++++++++++++++++++++++++++-------------------- 4 files changed, 130 insertions(+), 98 deletions(-) create mode 100644 cuedump.py delete mode 100644 cueread.py diff --git a/cue.py b/cue.py index 493bee9..4239084 100644 --- a/cue.py +++ b/cue.py @@ -27,7 +27,9 @@ class Track: return sort_iter(self._indexes) def get(self, attr): - return self._attrs.get(attr) + return self._attrs.get(attr, + None if attr in ("pregap", "postgap") else "" + ) def isaudio(self): return self.type == "AUDIO" and self.begin is not None @@ -38,8 +40,8 @@ class File: self.type = filetype self._tracks = [] - def tracks(self, audio_only = False): - return filter(Track.isaudio if audio_only else None, self._tracks) + def tracks(self, filter_audio = True): + return filter(Track.isaudio if filter_audio else None, self._tracks) def add_track(self, track): self._tracks.append(track) @@ -48,12 +50,12 @@ class File: return self.type == "WAVE" def has_audio_tracks(self): - return len(list(self.tracks(Track))) > 0 + return len(list(self.tracks())) > 0 def split_points(self, info): rate = info.sample_rate * info.bits_per_sample * info.channels // 8 - for track in list(self.tracks(True))[1:]: + for track in list(self.tracks())[1:]: yield rate * track.begin // 75 def __repr__(self): @@ -67,11 +69,11 @@ class Cue: def attrs(self): return sort_iter(self._attrs) - def files(self, audio_only = False): - return filter(File.isaudio if audio_only else None, self._files) + def files(self, filter_audio = True): + return filter(File.isaudio if filter_audio else None, self._files) def get(self, attr): - return self._attrs.get(attr) + return self._attrs.get(attr, "") def add_file(self, file): self._files.append(file) diff --git a/cuedump.py b/cuedump.py new file mode 100644 index 0000000..660603e --- /dev/null +++ b/cuedump.py @@ -0,0 +1,64 @@ +from os.path import basename +import sys + +from cue import read_cue + +if sys.version_info.major == 2: + class Encoded: + def __init__(self, stream): + self.stream = stream + + def write(self, msg): + self.stream.write(msg.encode("utf-8")) + + def __getattr__(self, attr): + return getattr(self.stream, attr) + + sys.stdout = Encoded(sys.stdout) + +def printf(fmt, *args): + sys.stdout.write(fmt % args) + +def msf(ts): + m = ts / (60 * 75) + s = ts / 75 % 60 + f = ts % 75 + + return "%d:%d:%d" % (m, s, f) + +def quote(s): + return s if " " not in s else "\"%s\"" % s + +progname = basename(sys.argv[0]) +if len(sys.argv) != 2: + printf("Usage: %s cuefile\n", progname) + sys.exit(1) + +try: + cue = read_cue(sys.argv[1], on_error = lambda err:\ + sys.stderr.write("** %s:%d: %s\n" % (progname, err.line, err)) + ) +except Exception as err: + printf("%s: read_cue failed: %s: %s\n", progname, err.__class__.__name__, err) + sys.exit(1) + +printf("Cue attributes:\n") +for k, v in cue.attrs(): + printf("\t%s = %s\n", k, quote(v)) + +for file in cue.files(filter_audio=False): + printf("File %s %s\n", quote(file.name), file.type) + for track in file.tracks(filter_audio=False): + printf("\tTrack %d\n", track.number) + pregap = track.get("pregap") + postgap = track.get("postgap") + for k, v in track.attrs(): + if k not in ("pregap", "postgap"): + printf("\t\t%s = %s\n", k, quote(v)) + if pregap is not None: + printf("\t\tPregap %s\n", msf(pregap)) + for k, v in track.indexes(): + printf("\t\tIndex %d %s\n", k, msf(v)) + if postgap is not None: + printf("\t\tPostgap %s\n", msf(postgap)) +sys.exit(0) diff --git a/cueread.py b/cueread.py deleted file mode 100644 index 902d10c..0000000 --- a/cueread.py +++ /dev/null @@ -1,64 +0,0 @@ -from os.path import basename -import sys - -from cue import read_cue - -if sys.version_info.major == 2: - class Encoded: - def __init__(self, stream): - self.stream = stream - - def write(self, msg): - self.stream.write(msg.encode("utf-8")) - - def __getattr__(self, attr): - return getattr(self.stream, attr) - - sys.stdout = Encoded(sys.stdout) - -def printf(fmt, *args): - sys.stdout.write(fmt % args) - -def msf(ts): - m = ts / (60 * 75) - s = ts / 75 % 60 - f = ts % 75 - - return "%d:%d:%d" % (m, s, f) - -def quote(s): - return s if " " not in s else "\"%s\"" % s - -progname = basename(sys.argv[0]) -if len(sys.argv) != 2: - printf("Usage: %s cuefile\n", progname) - sys.exit(1) - -try: - cue = read_cue(sys.argv[1], on_error = lambda err:\ - sys.stderr.write("** %s:%d: %s\n" % (progname, err.line, err)) - ) -except Exception as err: - printf("%s: read_cue failed: %s: %s\n", progname, err.__class__.__name__, err) - sys.exit(1) - -printf("Cue attributes:\n") -for k, v in cue.attrs(): - printf("\t%s = %s\n", k, quote(v)) - -for file in cue.files(): - printf("File %s %s\n", quote(file.name), file.type) - for track in file.tracks(): - printf("\tTrack %d\n", track.number) - pregap = track.get("pregap") - postgap = track.get("postgap") - for k, v in track.attrs(): - if k not in ("pregap", "postgap"): - printf("\t\t%s = %s\n", k, quote(v)) - if pregap is not None: - printf("\t\tPregap %s\n", msf(pregap)) - for k, v in track.indexes(): - printf("\t\tIndex %d %s\n", k, msf(v)) - if postgap is not None: - printf("\t\tPostgap %s\n", msf(postgap)) -sys.exit(0) diff --git a/cutter b/cutter index 3139502..1da3495 100755 --- a/cutter +++ b/cutter @@ -7,6 +7,7 @@ from optparse import OptionParser, OptionGroup from subprocess import Popen, PIPE from tempfile import mkdtemp from shutil import copyfile +from itertools import chain import sys import os @@ -74,7 +75,7 @@ def print_cue(cue): for k, v in cue.attrs(): printf("%s: %s\n", k.upper(), quote(v)) - for file in cue.files(audio_only = True): + for file in cue.files(): name = cue.dir + file.name printf("FILE %s", quote(file.name)) @@ -91,7 +92,7 @@ def print_cue(cue): info.sample_rate, info.channels) - for track in file.tracks(audio_only = True): + for track in file.tracks(): printf("\tTRACK %02d", track.number) title = track.get("title") if title != "": @@ -112,8 +113,9 @@ def parse_args(): help="ignore cue parsing errors") parser.add_option("--dump", - action="store_true", default=False, dest="dump", - help="print the content of cue file") + 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") @@ -143,12 +145,11 @@ def parse_args(): parser.add_option_group(format) tag = OptionGroup(parser, "Tag options") + tag_options = ["album", "artist", "date", "genre", + "comment", "composer", "albumartist"] - tag.add_option("--album", dest="album") - tag.add_option("--artist", dest="artist") - tag.add_option("--date", dest="date") - tag.add_option("--genre", dest="genre") - tag.add_option("--comment", dest="comment") + for opt in tag_options: + tag.add_option("--" + opt, dest=opt, default="") parser.add_option_group(tag) @@ -224,15 +225,18 @@ class CueSplitter: self.cue = cue self.opt = opt self.tracknumber = 0 - self.tracktotal = sum( - [len(list(f.tracks(True))) for f in self.cue.files(True)] - ) + self.tracktotal = 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"), "genre": self.opt.genre or self.cue.get("genre"), - "comment": self.opt.comment or self.cue.get("comment") + "comment": self.opt.comment or self.cue.get("comment"), + "composer": self.opt.composer + or self.cue.get("songwriter"), + "artist": self.opt.albumartist or self.opt.artist + or self.cue.get("artist"), + "albumartist": self.opt.albumartist } def find_realfile(self, name): @@ -250,7 +254,7 @@ class CueSplitter: def open_files(self): lst = [] - for file in self.cue.files(True): + for file in self.cue.files(): if not file.has_audio_tracks(): debug("skip file %s: no tracks", quote(file.name)) continue @@ -281,22 +285,28 @@ class CueSplitter: title = track.get("title") or "track" return "%02d.%s.flac" % (self.tracknumber, title) - def tag(self, track, path): - self.tags.update({ - "tracknumber": "%02d" % self.tracknumber, - "tracktotal": "%02d" % self.tracktotal, + def get_track_tags(self, track): + tags = dict(self.tags) + tags.update({ + "tracknumber": self.tracknumber, + "tracktotal": self.tracktotal, "title": track.get("title"), "artist": self.opt.artist or track.get("performer") or self.cue.get("performer"), + "composer": self.opt.composer or track.get("songwriter") }) + return tags + + def tag(self, track, path): + tags = self.get_track_tags(track) args = ["metaflac", "--remove-all-tags", "--import-tags-from=-", path] printf("Tag [%s] : ", path) sys.stdout.flush() proc = Popen(args, stdin = PIPE) - for k, v in self.tags.items(): - if v is not None and v != "": + for k, v in tags.items(): + if v is not "": proc.stdin.write(to_bytes("%s=%s\n" % (k.upper(), v))) proc.stdin.close() @@ -318,7 +328,7 @@ class CueSplitter: if noteq(self.opt.channels, file.info.channels): return False - track = list(file.tracks(True))[0] + track = list(file.tracks())[0] trackname = self.get_track_name(track) dest = os.path.join(self.opt.dir, trackname) @@ -383,7 +393,7 @@ class CueSplitter: sys.exit(1) splitted = filterdir(self.opt.dir, "split-track") - for track, filename in zip(file.tracks(True), splitted): + for track, filename in zip(file.tracks(), splitted): trackname = self.get_track_name(track) dest = os.path.join(self.opt.dir, trackname) @@ -410,6 +420,24 @@ class CueSplitter: else: self.split_file(file, points) + def all_tracks(self): + return chain(*[f.tracks() for f in self.cue.files()]) + + def dump_tags(self): + for track in self.all_tracks(): + if self.tracknumber: + printf("\n") + + self.tracknumber += 1 + tags = self.get_track_tags(track) + for k, v in sorted(tags.items()): + if v is not "": + printf("%s=%s\n", k.upper(), v) + def dump_tracks(self): + for track in self.all_tracks(): + trackname = self.get_track_name(track) + printf("%s\n", os.path.join(self.opt.dir, trackname)) + def main(): options, args = parse_args() verify_options(options) @@ -439,10 +467,12 @@ def main(): if cue.dir: cue.dir += "/" - if options.dump: - print_cue(cue) - else: - CueSplitter(cue, options).split() + { + "cue": lambda: print_cue(cue), + "tags": lambda: CueSplitter(cue, options).dump_tags(), + "tracks": lambda: CueSplitter(cue, options).dump_tracks(), + None: lambda: CueSplitter(cue, options).split() + }[options.dump]() return 0 -- cgit v1.2.3-70-g09d2