Example #1
0
 def parse_meta(self, lines):
     self.meta = OrderedDict()
     for key, val in self.keyvalues(lines):
         lang = None
         m = re.match(r"(.*)\[(.*)\]", key)
         if m:
             key = m.group(1)
             lang = m.group(2)
         if key not in self.meta:
             self.meta[key] = MultiString()
         self.meta[key][lang] = val
Example #2
0
	def parse_meta(self, lines):
		self.meta = OrderedDict()
		for key, val in self.keyvalues(lines):
			lang = None
			m = re.match(r"(.*)\[(.*)\]", key)
			if m:
				key = m.group(1)
				lang = m.group(2)
			if key not in self.meta:
				self.meta[key] = MultiString()
			self.meta[key][lang] = val
Example #3
0
 def parse_twolevel(self, lines):
     inner = None
     for line in lines:
         if not line:
             continue
         if line[0] == "{":
             if line[-1] != "}":
                 raise ParseError("Expected {...} (%s)" % line)
             if inner is not None:
                 yield name, inner
             name = line[1:-1]
             inner = OrderedDict()
             continue
         if inner is None:
             raise ParseError("Expected {...} (%s)" % line)
         if "=" not in line:
             raise ParseError("Expected key=value format (%s)" % line)
         key, val = line.split("=", 1)
         inner[key] = val
     if inner is not None:
         yield name, inner
Example #4
0
	def parse_variants(self, lines):
		self.variants = OrderedDict()
		for name, data in self.parse_twolevel(lines):
			self.variants[name] = Variant(data)
Example #5
0
	def parse_styles(self, lines):
		self.styles = OrderedDict()
		for name, data in self.parse_twolevel(lines):
			self.styles[name] = Style(data)
Example #6
0
	def parse_formats(self, lines):
		self.formats = OrderedDict()
		for tag, fmt in self.keyvalues(lines):
			if fmt not in FORMATS:
				raise ParseError("Unknown format %s" % fmt)
			self.formats[tag] = FORMATS[fmt]
Example #7
0
	def parse_song(self, lines):
		self.song = OrderedDict()
		for key, val in self.keyvalues(lines):
			self.song[key] = val
Example #8
0
class Song(object):
	def __init__(self, filename, ignore_steps=False):
		parsers = {
			"Meta": self.parse_meta,
			"Song": self.parse_song,
			"Timing": self.parse_timing,
			"Formats": self.parse_formats,
			"Styles": self.parse_styles,
			"Variants": self.parse_variants,
			"Lyrics": self.parse_lyrics,
		}
		self.ignore_steps = ignore_steps
		self.pathbase = os.path.dirname(filename)
		section = None
		lines = []
		self.meta = None
		self.song = None
		self.timing = None
		self.formats = None
		self.styles = None
		self.variants = None
		self.compounds = None

		self.fake_time = 0
		self.line = 0
		self.section_line = 0
		for line in codecs.open(filename, encoding='utf-8', mode='r'):
			self.line += 1
			line = line.replace("\n","").replace("\r","")
			if section is None:
				if not line:
					continue
				if line[0] != "[" or line[-1] != "]":
					raise ParseError("Expected section header")
				section = line[1:-1]
				self.section_line = self.line
				continue
			if line and line[0] == "[" and line[-1] == "]":
				if section not in parsers:
					raise ParseError("Unknown section %s" % section)
				parsers[section](lines)
				lines = []
				section = line[1:-1]
				self.section_line = self.line
			else:
				lines.append(line)
		if section is not None:
			parsers[section](lines)

		if self.variants and self.styles:
			for variant in self.variants.values():
				variant.load_tags(self.styles)

	def keyvalues(self, lines):
		for line in lines:
			if not line:
				continue
			if "=" not in line:
				raise ParseError("Expected key=value format (%s)" % line)
			key, val = line.split("=", 1)
			yield key, val

	def parse_meta(self, lines):
		self.meta = OrderedDict()
		for key, val in self.keyvalues(lines):
			lang = None
			m = re.match(r"(.*)\[(.*)\]", key)
			if m:
				key = m.group(1)
				lang = m.group(2)
			if key not in self.meta:
				self.meta[key] = MultiString()
			self.meta[key][lang] = val

	def parse_song(self, lines):
		self.song = OrderedDict()
		for key, val in self.keyvalues(lines):
			self.song[key] = val

	def parse_timing(self, lines):
		self.timing = BeatCounter()
		for time, beat in self.keyvalues(lines):
			if time[0] != "@":
				raise ParseError("Expected @time")
			self.timing.add(float(time[1:]), int(beat))

	def parse_formats(self, lines):
		self.formats = OrderedDict()
		for tag, fmt in self.keyvalues(lines):
			if fmt not in FORMATS:
				raise ParseError("Unknown format %s" % fmt)
			self.formats[tag] = FORMATS[fmt]

	def parse_twolevel(self, lines):
		inner = None
		for line in lines:
			if not line:
				continue
			if line[0] == "{":
				if line[-1] != "}":
					raise ParseError("Expected {...} (%s)" % line)
				if inner is not None:
					yield name, inner
				name = line[1:-1]
				inner = OrderedDict()
				continue
			if inner is None:
				raise ParseError("Expected {...} (%s)" % line)
			if "=" not in line:
				raise ParseError("Expected key=value format (%s)" % line)
			key, val = line.split("=", 1)
			inner[key] = val
		if inner is not None:
			yield name, inner

	def parse_styles(self, lines):
		self.styles = OrderedDict()
		for name, data in self.parse_twolevel(lines):
			self.styles[name] = Style(data)

	def parse_variants(self, lines):
		self.variants = OrderedDict()
		for name, data in self.parse_twolevel(lines):
			self.variants[name] = Variant(data)

	def parse_lyrics(self, lines):
		self.compounds = []
		compounds = None
		compound = None
		lineno = self.section_line
		for s in (lines + [""]):
			lineno += 1
			if not s and compound:
				first = compound[compound.keys()[0]]
				for key,val in compound.items():
					if first.steps != val.steps:
						raise ParseError("%d: Duration mismatch: %d!=%d (%s) (%s)" % (lineno, first.steps, val.steps, unicode(first), unicode(val)))
				if compound.timing is not None:
					if compound.steps != len(compound.timing):
						raise ParseError("%d: Timing line length mismatch: %d!=%d" % (lineno, len(compound.timing), compound.steps))
				else:
					compound.start = self.fake_time
					self.fake_time += compound.steps
				compound = None

			if not s:
				continue
			if compound is None:
				compound = Compound(self.timing)
				self.compounds.append(compound)

			if ":" not in s:
				raise ParseError("Expected 'X: value': %r" % s)

			tag, text = s.split(":", 1)
			tag = tag.strip()
			text = text.strip()

			if tag == "@":
				if self.ignore_steps:
					continue
				timing = map(MixedFraction, text.split())
				compound.start = timing[0]
				compound.timing = timing[1:]
				if compound:
					first = compound[compound.keys()[0]]
					if first.steps != len(compound.timing):
						raise ParseError("%d: Timing line length mismatch: %d!=%d" % (lineno, len(compound.timing), compound.steps))
				continue

			if tag not in self.formats:
				raise ParseError("Undefined format %r" % tag)

			compound[tag] = self.formats[tag](text)

	def dump(self):
		s = ""
		if self.meta is not None:
			s += "[Meta]\n"
			for tag, value in self.meta.items():
				for lang, text in value.items():
					if lang is None:
						s += u"%s=%s\n" % (tag, text)
					else:
						s += u"%s[%s]=%s\n" % (tag, lang, text)
			s += "\n"
		if self.song is not None:
			s += "[Song]\n"
			for tag, value in self.song.items():
				s += "%s=%s\n" % (tag, value)
			s += "\n"
		if self.timing is not None:
			s += "[Timing]\n"
			for time, beat in self.timing.beats:
				s += "@%f=%d\n" % (time, beat)
			s += "\n"
		if self.formats is not None:
			s += "[Formats]\n"
			for tag, fmt in self.formats.items():
				s += "%s=%s\n" % (tag, I_FORMATS[fmt])
			s += "\n"
		if self.styles is not None:
			s += "[Styles]\n"
			for name, style in self.styles.items():
				s += "{%s}\n" % name
				for key, value in style.data.items():
					s += "%s=%s\n" % (key, value)
				s += "\n"
		if self.variants is not None:
			s += "[Variants]\n"
			for name, variant in self.variants.items():
				s += "{%s}\n" % name
				for key, value in variant.data.items():
					s += u"%s=%s\n" % (key, value)
				s += "\n"
		if self.compounds is not None:
			s += "[Lyrics]\n\n"
			for compound in self.compounds:
				for tag, molecule in compound.items():
					s += u"%s: %s\n" % (tag, molecule.source)
				if compound.timing is not None:
					s += u"@: %s  %s\n" % (str(compound.start), ' '.join(map(str,compound.timing)))
				s += "\n"
				if any(i.break_before or i.break_after for i in compound.values()):
					s += "\n"
		return s

	def save(self, filename):
		fd = open(filename, "w")
		fd.write(self.dump().encode("utf-8"))
		fd.close()

	@property
	def audiofile(self):
		return os.path.join(self.pathbase, self.song["audio"])

	@property
	def videofile(self):
		if "video" not in self.song:
			return None
		else:
			return os.path.join(self.pathbase, self.song["video"])

	@property
	def coverfile(self):
		if "cover" not in self.song:
			return None
		else:
			return os.path.join(self.pathbase, self.song["cover"])

	@property
	def aspect(self):
		if "aspect" not in self.song:
			return None
		else:
			return fractions.Fraction(self.song["aspect"])

	def get_lyric_snippet(self, variant_id, length=100):
		variant = self.variants[variant_id]
		tags = set(i for i in variant.tag_list if variant.tags[i].edge == TagInfo.BOTTOM)
		lyrics = ""
		broke = False
		for compound in self.compounds:
			if len(lyrics) >= length:
				break
			for tag, molecule in compound.items():
				if tag not in tags:
					continue
				if molecule.break_before and not broke:
					lyrics += u"/" + molecule.SPACE
				broke = False
				lyrics += molecule.text + molecule.SPACE
				if molecule.break_after:
					lyrics += u"/" + molecule.SPACE
					broke = True
					
		return lyrics

	def get_font_path(self, font):
		song_font = os.path.join(self.pathbase, font)
		if os.path.exists(song_font):
			return song_font
		cwd_font = font
		if os.path.exists(cwd_font):
			return cwd_font
		raise IOError("Font %s not found" % font)
Example #9
0
	def __init__(self, song_timing):
		OrderedDict.__init__(self)
		self.start = None
		self.timing = None
		self.song_timing = song_timing
Example #10
0
 def parse_variants(self, lines):
     self.variants = OrderedDict()
     for name, data in self.parse_twolevel(lines):
         self.variants[name] = Variant(data)
Example #11
0
 def parse_styles(self, lines):
     self.styles = OrderedDict()
     for name, data in self.parse_twolevel(lines):
         self.styles[name] = Style(data)
Example #12
0
 def parse_formats(self, lines):
     self.formats = OrderedDict()
     for tag, fmt in self.keyvalues(lines):
         if fmt not in FORMATS:
             raise ParseError("Unknown format %s" % fmt)
         self.formats[tag] = FORMATS[fmt]
Example #13
0
 def parse_song(self, lines):
     self.song = OrderedDict()
     for key, val in self.keyvalues(lines):
         self.song[key] = val
Example #14
0
class Song(object):
    def __init__(self, filename, ignore_steps=False):
        parsers = {
            "Meta": self.parse_meta,
            "Song": self.parse_song,
            "Timing": self.parse_timing,
            "Formats": self.parse_formats,
            "Styles": self.parse_styles,
            "Variants": self.parse_variants,
            "Lyrics": self.parse_lyrics,
        }
        self.ignore_steps = ignore_steps
        self.pathbase = os.path.dirname(filename)
        section = None
        lines = []
        self.meta = None
        self.song = None
        self.timing = None
        self.formats = None
        self.styles = None
        self.variants = None
        self.compounds = None

        self.fake_time = 0
        self.line = 0
        self.section_line = 0
        for line in codecs.open(filename, encoding='utf-8', mode='r'):
            self.line += 1
            line = line.replace("\n", "").replace("\r", "")
            if line.startswith("#"):
                continue
            if section is None:
                if not line:
                    continue
                if line[0] != "[" or line[-1] != "]":
                    raise ParseError("Expected section header")
                section = line[1:-1]
                self.section_line = self.line
                continue
            if line and line[0] == "[" and line[-1] == "]":
                if section not in parsers:
                    raise ParseError("Unknown section %s" % section)
                parsers[section](lines)
                lines = []
                section = line[1:-1]
                self.section_line = self.line
            else:
                lines.append(line)
        if section is not None:
            parsers[section](lines)

        if self.variants and self.styles:
            for variant in self.variants.values():
                variant.load_tags(self.styles)

    def keyvalues(self, lines):
        for line in lines:
            if not line:
                continue
            if "=" not in line:
                raise ParseError("Expected key=value format (%s)" % line)
            key, val = line.split("=", 1)
            yield key, val

    def parse_meta(self, lines):
        self.meta = OrderedDict()
        for key, val in self.keyvalues(lines):
            lang = None
            m = re.match(r"(.*)\[(.*)\]", key)
            if m:
                key = m.group(1)
                lang = m.group(2)
            if key not in self.meta:
                self.meta[key] = MultiString()
            self.meta[key][lang] = val

    def parse_song(self, lines):
        self.song = OrderedDict()
        for key, val in self.keyvalues(lines):
            self.song[key] = val

    def parse_timing(self, lines):
        self.timing = BeatCounter()
        for time, beat in self.keyvalues(lines):
            if time[0] != "@":
                raise ParseError("Expected @time")
            self.timing.add(float(time[1:]), int(beat))

    def parse_formats(self, lines):
        self.formats = OrderedDict()
        for tag, fmt in self.keyvalues(lines):
            if fmt not in FORMATS:
                raise ParseError("Unknown format %s" % fmt)
            self.formats[tag] = FORMATS[fmt]

    def parse_twolevel(self, lines):
        inner = None
        for line in lines:
            if not line:
                continue
            if line[0] == "{":
                if line[-1] != "}":
                    raise ParseError("Expected {...} (%s)" % line)
                if inner is not None:
                    yield name, inner
                name = line[1:-1]
                inner = OrderedDict()
                continue
            if inner is None:
                raise ParseError("Expected {...} (%s)" % line)
            if "=" not in line:
                raise ParseError("Expected key=value format (%s)" % line)
            key, val = line.split("=", 1)
            inner[key] = val
        if inner is not None:
            yield name, inner

    def parse_styles(self, lines):
        self.styles = OrderedDict()
        for name, data in self.parse_twolevel(lines):
            self.styles[name] = Style(data)

    def parse_variants(self, lines):
        self.variants = OrderedDict()
        for name, data in self.parse_twolevel(lines):
            self.variants[name] = Variant(data)

    def parse_lyrics(self, lines):
        self.compounds = []
        compounds = None
        compound = None
        lineno = self.section_line
        for s in (lines + [""]):
            lineno += 1
            if not s and compound:
                first = compound[compound.keys()[0]]
                for key, val in compound.items():
                    if first.steps != val.steps:
                        raise ParseError(
                            "%d: Duration mismatch: %d!=%d (%s) (%s)" %
                            (lineno, first.steps, val.steps, unicode(first),
                             unicode(val)))
                if compound.timing is not None:
                    if compound.steps != len(compound.timing):
                        raise ParseError(
                            "%d: Timing line length mismatch: %d!=%d" %
                            (lineno, len(compound.timing), compound.steps))
                else:
                    compound.start = self.fake_time
                    self.fake_time += compound.steps
                compound = None

            if not s:
                continue
            if compound is None:
                compound = Compound(self.timing)
                self.compounds.append(compound)

            if ":" not in s:
                raise ParseError("Expected 'X: value': %r" % s)

            tag, text = s.split(":", 1)
            tag = tag.strip()
            text = text.strip()

            if tag == "@":
                if self.ignore_steps:
                    continue
                timing = map(MixedFraction, text.split())
                compound.start = timing[0]
                compound.timing = timing[1:]
                if compound:
                    first = compound[compound.keys()[0]]
                    if first.steps != len(compound.timing):
                        raise ParseError(
                            "%d: Timing line length mismatch: %d!=%d" %
                            (lineno, len(compound.timing), compound.steps))
                continue

            if tag not in self.formats:
                raise ParseError("Undefined format %r" % tag)

            compound[tag] = self.formats[tag](text)

    def dump(self):
        s = ""
        if self.meta is not None:
            s += "[Meta]\n"
            for tag, value in self.meta.items():
                for lang, text in value.items():
                    if lang is None:
                        s += u"%s=%s\n" % (tag, text)
                    else:
                        s += u"%s[%s]=%s\n" % (tag, lang, text)
            s += "\n"
        if self.song is not None:
            s += "[Song]\n"
            for tag, value in self.song.items():
                s += "%s=%s\n" % (tag, value)
            s += "\n"
        if self.timing is not None:
            s += "[Timing]\n"
            for time, beat in self.timing.beats:
                s += "@%f=%d\n" % (time, beat)
            s += "\n"
        if self.formats is not None:
            s += "[Formats]\n"
            for tag, fmt in self.formats.items():
                s += "%s=%s\n" % (tag, I_FORMATS[fmt])
            s += "\n"
        if self.styles is not None:
            s += "[Styles]\n"
            for name, style in self.styles.items():
                s += "{%s}\n" % name
                for key, value in style.data.items():
                    s += "%s=%s\n" % (key, value)
                s += "\n"
        if self.variants is not None:
            s += "[Variants]\n"
            for name, variant in self.variants.items():
                s += "{%s}\n" % name
                for key, value in variant.data.items():
                    s += u"%s=%s\n" % (key, value)
                s += "\n"
        if self.compounds is not None:
            s += "[Lyrics]\n\n"
            for compound in self.compounds:
                for tag, molecule in compound.items():
                    s += u"%s: %s\n" % (tag, molecule.source)
                if compound.timing is not None:
                    s += u"@: %s  %s\n" % (str(compound.start), ' '.join(
                        map(str, compound.timing)))
                s += "\n"
                if any(i.break_before or i.break_after
                       for i in compound.values()):
                    s += "\n"
        return s

    def save(self, filename):
        fd = open(filename, "w")
        fd.write(self.dump().encode("utf-8"))
        fd.close()

    @property
    def audiofile(self):
        return os.path.join(self.pathbase, self.song["audio"])

    @property
    def videofile(self):
        if "video" not in self.song:
            return None
        else:
            return os.path.join(self.pathbase, self.song["video"])

    @property
    def coverfile(self):
        if "cover" not in self.song:
            return None
        else:
            return os.path.join(self.pathbase, self.song["cover"])

    @property
    def aspect(self):
        if "aspect" not in self.song:
            return None
        else:
            return fractions.Fraction(self.song["aspect"])

    def get_lyric_snippet(self, variant_id, length=100):
        variant = self.variants[variant_id]
        tags = set(i for i in variant.tag_list
                   if variant.tags[i].edge == TagInfo.BOTTOM)
        lyrics = ""
        broke = False
        for compound in self.compounds:
            if len(lyrics) >= length:
                break
            for tag, molecule in compound.items():
                if tag not in tags:
                    continue
                if molecule.break_before and not broke:
                    lyrics += u"/" + molecule.SPACE
                broke = False
                lyrics += molecule.text + molecule.SPACE
                if molecule.break_after:
                    lyrics += u"/" + molecule.SPACE
                    broke = True

        return lyrics

    def get_font_path(self, font):
        song_font = os.path.join(self.pathbase, font)
        if os.path.exists(song_font):
            return song_font
        cwd_font = font
        if os.path.exists(cwd_font):
            return cwd_font
        raise IOError("Font %s not found" % font)
Example #15
0
 def __init__(self, song_timing):
     OrderedDict.__init__(self)
     self.start = None
     self.timing = None
     self.song_timing = song_timing
Example #16
0
    def __init__(self, filename=None, ignore_steps=False):
        parsers = {
            "Meta": self.parse_meta,
            "Song": self.parse_song,
            "Timing": self.parse_timing,
            "Formats": self.parse_formats,
            "Styles": self.parse_styles,
            "Variants": self.parse_variants,
            "Lyrics": self.parse_lyrics,
        }
        self.ignore_steps = ignore_steps
        self.pathbase = os.path.dirname(filename) if filename else None
        section = None
        lines = []
        self.meta = None
        self.song = None
        self.timing = None
        self.formats = None
        self.styles = None
        self.variants = None
        self.compounds = None

        self.fake_time = 0
        self.line = 0
        self.section_line = 0

        if not filename:
            self.meta = OrderedDict()
            self.song = OrderedDict()
            self.timing = BeatCounter()
            self.formats = OrderedDict()
            self.styles = OrderedDict()
            self.variants = OrderedDict()
            self.compounds = []
            return

        for line in codecs.open(filename, encoding='utf-8', mode='r'):
            self.line += 1
            line = line.replace("\n","").replace("\r","")
            if line.startswith("#"):
                continue
            if section is None:
                if not line:
                    continue
                if line[0] != "[" or line[-1] != "]":
                    raise ParseError("Expected section header")
                section = line[1:-1]
                self.section_line = self.line
                continue
            if line and line[0] == "[" and line[-1] == "]":
                if section not in parsers:
                    raise ParseError("Unknown section %s" % section)
                parsers[section](lines)
                lines = []
                section = line[1:-1]
                self.section_line = self.line
            else:
                lines.append(line)
        if section is not None:
            parsers[section](lines)

        if self.variants and self.styles:
            for variant in self.variants.values():
                variant.load_tags(self.styles)