From ebe8783d8096680153d53b7dca4261dab069b242 Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Sat, 29 Sep 2007 19:45:57 +0200 Subject: Initial commit. Classes for script, info, styles and events with karaoke are implemented. --- pykfx.py | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 pykfx.py diff --git a/pykfx.py b/pykfx.py new file mode 100644 index 0000000..0f8d0a0 --- /dev/null +++ b/pykfx.py @@ -0,0 +1,174 @@ +import re + +class StyleException(Exception): pass +class EventException(Exception): pass +class ScriptException(Exception): pass + +class ScriptInfo: + def __init__(self, f = None): + self.info = {} + if f: + self.read(f) + + def __getitem__(self, key): + try: + return self.info[key.lower()] + except ValueError: + raise KeyError + + def read(self, f): + s = f.readline().strip() + while s: + if not s.startswith(';'): + k, v = s.split(': ', 1) + self.info[k.lower()] = v + s = f.readline().strip() + +class Style: + def __init__(self, format = None, s = None): + if format and s: + self.parse(format, s) + + def parse(self, format, s): + s = s.split(': ', 1) + if s[0] != 'Style': + raise StyleException('invalid style line') + s = s[1].split(',') + for k, v in zip(format, s): + setattr(self, k, v) + +class Styles: + def __init__(self, f = None): + self.format, self.styles = [], {} + if f: + self.read(f) + + def __getitem__(self, key): + if self.styles.has_key(key.lower()): + return self.styles[key] + else: + raise KeyError('style "%s" not found' % key) + + def read(self, f): + s = f.readline().strip() + s = s.split(': ', 1) + if s[0] != 'Format': + raise StyleException('format line not found') + self.format = [x.lower() for x in s[1].split(', ')] + + s = f.readline().strip() + while s: + s = Style(self.format, s) + self.styles[s.name.lower()] = s + s = f.readline().split() + +class Syllable: + def __init__(self, start, end, text): + self.start, self.end, self.dur, self.text = start, end, end - start, text + + def __str__(self): + return '{\\k%d}%s' % (self.dur / 10, self.text) + +class Event: + def __init__(self, format = None, s = None): + self.karaoke = [] + if format and s: + self.parse(format, s) + + def __getitem__(self, key): + if type(key) is int: + return self.karaoke[key] + else: + raise TypeError('key must be int') + + def parse(self, format, s): + s = s.split(': ', 1) + self.kind = s[0].lower() + s = s[1].split(',') + for k, v in zip(format, s): + setattr(self, k, v) + if self.kind == 'dialogue' and self.text.find('\\k') > -1: + self.parse_karaoke() + + def parse_karaoke(self): + s = self.text + i = 0 + tot = 0 + while i < len(s): + i = s.find('\\k', i) + # break if no more \k's (end-of-line-text is added at the end of the loop) + if i == -1: + break + i += 2 + ms = '' + while s[i].isdigit(): + ms += s[i] + i += 1 + ms = int(ms) * 10 + tot += ms + # unless next is another override, skip one char (past '}') + if s[i] != '\\': + i += 1 + n = s.find('\\k', i) + if n > -1: + part = s[i:n-1] # get text up to next \k + else: + part = s[i:] # end of line + + # go to next if part is empty + if not part.strip(): + continue + + self.karaoke.append(Syllable(tot - ms, tot, part)) + +class Events: + def __init__(self, f = None): + self.format, self.events = [], [] + if f: + self.read(f) + + def __getitem__(self, key): + if type(key) is int: + return self.events[key] + else: + raise TypeError('key must be int') + + def read(self, f): + s = f.readline().strip() + s = s.split(': ', 1) + if s[0] != 'Format': + raise EventException('format line not found') + self.format = [x.lower() for x in s[1].split(', ')] + + s = f.readline().strip() + while s: + self.events.append(Event(self.format, s)) + s = f.readline().strip() + +class Script: + def __init__(self, f = None): + if f: + self.read(f) + + def read(self, f): + f = open(f, 'r') + + s = f.readline().strip() + if s.startswith('\xef\xbb\xbf'): + s = s[3:] + if s == '[Script Info]': + self.info = ScriptInfo(f) + else: + raise ScriptException('script info not found') + + s = f.readline().strip() + if re.match('\[V4\+? Styles\]', s): + self.styles = Styles(f) + else: + raise ScriptException('script styles not found') + + s = f.readline().strip() + if s == '[Events]': + self.events = Events(f) + else: + raise ScriptException('script events not found') -- cgit v1.2.3