diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | cue.py | 210 |
2 files changed, 214 insertions, 0 deletions
@@ -33,3 +33,7 @@ nosetests.xml .mr.developer.cfg .project .pydevproject + +# after vim +*.swp +*~ @@ -0,0 +1,210 @@ +import sys + +def printf(fmt, *args): + sys.stdout.write(fmt % args) + +class Track: + def __init__(self, number, datatype): + try: + self.number = int(number) + except ValueError: + raise InvalidCommand("invalid number \"%s\"" % number) + + self.datatype = datatype + self.indexes = [] + + self.performer = "" + self.title = "" + + def get_number(): + return self.number + +class File: + def __init__(self, name, filetype): + self.name = name + self.filetype = filetype + self.tracks = [] + + def __repr__(self): + return self.name + + def get_type(self): + return self.filetype + +class Cue: + def __init__(self): + self.performer = "" + self.title = "" + + self.tracks = [] + self.files = [] + + def get_tracks(self): + return self.tracks + +class CueParserError(Exception): + pass + +class UnknownCommand(CueParserError): + pass + +class InvalidCommand(CueParserError): + pass + +class InvalidContext(CueParserError): + pass + +def check_argc(count): + def deco(func): + def method(cls, *lst): + n = len(lst) + if n != count: + raise InvalidCommand("%d args expected, got %d" % (count, n)) + func(cls, *lst) + return method + return deco + +class CueParser: + ( + GENERAL, + TRACK + FILE, + ) = range(3) + + def __init__(self, msg): + self.cue = Cue() + self.context = self.GENERAL + self.commands = { + "file": self.parse_file, + "track": self.parse_track, + "index": self.parse_index, + "title": self.parse_title, + "performer": self.parse_performer, + "flags": self.parse_flags + } + + self.msg = msg + + @staticmethod + def split_args(args): + lst = [] + quote = None + cur = [] + + def push(): + lst.append("".join(cur)) + cur[:] = [] + + for ch in args: + if quote: + if ch != quote: + cur.append(ch) + else: + quote = None + elif ch.isspace(): + if cur: + push() + elif ch in ("\"", "'"): + quote = ch + else: + cur.append(ch) + + if quote: + raise CueParserError("unclosed quote '%s'" % quote) + + if cur: + push() + + return lst + + def get_cue(): + return cue + + @check_argc(2) + def parse_file(self, *args): + self.last_file = File(*args) + self.cue.files.append(self.last_file) + self.context = self.FILE + + @check_argc(2) + def parse_track(self, *args): + if self.context != self.FILE: + raise InvalidContext + + self.last_track = Track(*args) + self.cue.tracks.append(self.last_track) + self.last_file.tracks.append(self.last_track) + self.context = self.TRACK + + def set_prop(self, attr, value, opt): + obj = opt.get(self.context) + if obj is None: + raise InvalidContext + + if getattr(obj, attr): + raise InvalidCommand("duplicate") + + setattr(obj, attr, value) + + @check_argc(1): + def parse_title(self, title): + self.set_prop("title", title, { + self.GENERAL: self.cue, + self.TRACK: self.last_track + }) + + @check_argc(1) + def parse_performer(self, performer): + self.set_prop("performer", performer, { + self.GENERAL: self.cue, + self.TRACK: self.last_track + }) + + def parse_flags(self, *flags): + if self.context != self.TRACK: + raise InvalidContext + if self.last_track.indexes: + raise InvalidCommand("must appear before any INDEX commands") + + def parse_default(self, args): + raise UnknownCommand + + def parse(self, cmd, arg): + self.commands.get(cmd.lower(), parse_default)(*self.split_args(arg)) + +def read_cuesheet(filename): + def msg(fmt, args): + printf("read_cuesheet %s:%d: ", filename, nline) + printf(fmt, args) + + if not fmt.endswith("\n"): + printf("\n") + + fp = open(filename) + parser = CueParser(msg) + nline = 0 + + for line in fp.readlines(): + nline = nline + 1 + s = line.strip() + if not len(s): + continue + + data = s.split(None, 1) + if len(data) is 1: + msg("arg missed") + continue + + try: + parser.parse(*data) + except UnknownCommand: + msg("unknown command \"%s\"", data[0]) + except InvalidContext: + msg("invalid context for command \"%s\"", data[0]) + except InvalidCommand as err: + msg("invalid command \"%s\": %s", data[0], err) + except CueParserError as err: + msg("%s", err) + + fp.close() + return parser.get_cue() |
