summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pykfx.py174
1 files changed, 174 insertions, 0 deletions
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')