From c062ac07e6241cf32a4a5655775aa037bdb1e76e Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Mon, 1 Oct 2007 00:04:47 +0200 Subject: Moved pykfx.py to pykfx/__init__.py Added self.names in Styles along with int support in __getitem__. Added setup.py. Added fscalc, an extension that calculates text sizes for a given font. Using boost.python-wrapper for fscalc. --- pykfx/__init__.py | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 pykfx/__init__.py (limited to 'pykfx/__init__.py') diff --git a/pykfx/__init__.py b/pykfx/__init__.py new file mode 100644 index 0000000..4ef55f1 --- /dev/null +++ b/pykfx/__init__.py @@ -0,0 +1,232 @@ +import re + +class StyleException(Exception): pass +class EventException(Exception): pass +class ScriptException(Exception): pass + +class ScriptInfo: + def __init__(self, f = None): + # original case keys used for writing + self.ock = {} + 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.ock[k.lower()] = k + self.info[k.lower()] = v + s = f.readline().strip() + + def write(self, f): + f.write('[Script Info]\n') + f.writelines(['%s: %s\n' % (self.ock[k], self.info[k]) for k in self.info.keys()]) + +class Style: + def __init__(self, format, s = None): + self.format = format + if s: + self.parse(s) + + def __str__(self): + return 'Style: %s' % ','.join([getattr(self, x.lower()) for x in self.format]) + + def parse(self, s): + s = s.split(': ', 1) + if s[0] != 'Style': + raise StyleException('invalid style line') + s = s[1].split(',') + for k, v in zip(self.format, s): + setattr(self, k.lower(), v) + +class Styles: + def __init__(self, f = None): + self.format, self.styles, self.names = [], {}, [] + if f: + self.read(f) + + def __getitem__(self, key): + if type(key) == int: + return self.styles[self.names[key]] + elif self.styles.has_key(key.lower()): + return self.styles[key.lower()] + 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 = s[1].split(', ') + + s = f.readline().strip() + while s: + s = Style(self.format, s) + self.styles[s.name.lower()] = s + self.names.append(s.name.lower()) + s = f.readline().split() + + def write(self, f): + f.write('[V4+ Styles]\n') + f.write('Format: %s\n' % ', '.join(self.format)) + f.writelines(['%s\n' % str(s) for s in self.styles.values()]) + +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, s = None): + self.karaoke = [] + self.format = format + if s: + self.parse(s) + + def __getitem__(self, key): + if type(key) is int: + return self.karaoke[key] + else: + raise TypeError('key must be int') + + def __str__(self): + return '%s: %s' % (self.kind.capitalize(), ','.join([getattr(self, x.lower()) for x in self.format])) + + def parse(self, s): + s = s.split(': ', 1) + self.kind = s[0].lower() + s = s[1].split(',') + for k, v in zip(self.format, s): + setattr(self, k.lower(), 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)) + + def add_lead_in(self, s): + t = self.start.split(':') + ts = float(t[-1]) + 60 * int(t[-2]) + 3600 * int(t[-3]) - s + th, ts = int(ts / 3600), ts % 3600 + tm, ts = int(ts / 60), ts % 60 + self.start = '%01d:%02d:%05.2f' % (th, tm, ts) + + def add_lead_out(self, s): + t = self.snd.split(':') + ts = float(t[-1]) + 60 * int(t[-2]) + 3600 * int(t[-3]) + s + th, ts = int(ts / 3600), ts % 3600 + tm, ts = int(ts / 60), ts % 60 + self.end = '%01d:%02d:%05.2f' % (th, tm, ts) + +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 add(self, s = None, e = None): + if not e and s: + e = Event(self.format, s) + if e: + self.events.append(e) + return e + + def read(self, f): + s = f.readline().strip() + s = s.split(': ', 1) + if s[0] != 'Format': + raise EventException('format line not found') + self.format = s[1].split(', ') + + s = f.readline().strip() + while s: + self.add(s) + s = f.readline().strip() + + def write(self, f): + f.write('[Events]\n') + f.write('Format: %s\n' % ', '.join(self.format)) + f.writelines(['%s\n' % str(e) for e in self.events]) + +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') + + def write(self, f): + f = open(f, 'w') + self.info.write(f) + f.write('\n') + self.styles.write(f) + f.write('\n') + self.events.write(f) + f.write('\n') -- cgit v1.2.3