import re import functools as ft from datetime import datetime import locale def join_list(the_list, separator="\n"): return ft.reduce(lambda x, y: x + (separator if x else "") + str(y), the_list, "") def readfile(file): with open(file, 'r') as f: return join_list(f.readlines(), '') locale.setlocale(locale.LC_ALL, "es_ES.UTF-8") song_template = readfile("res/song.html") class Song: def __init__(self, name, number, author=None, origin=None, latex_file=None, audios=None, capo=0, category=None): if audios is None: audios = [] self.verses = [] self.name = name.replace("\\\\", "-").replace("\\", "") self.number = number self.author = author self.origin = origin self.latex_file = latex_file self.audios = audios self.capo = capo self.category = category def __str__(self): return song_template.format( name=self.name, author="
Autor: %s
" % self.author if self.author else "", origin="
Basada en: %s
" % self.origin if self.origin else "", capo_settings="""
Tono original: Cejilla {s.capo}
""" .format(s=self) if self.capo != 0 else "", song_html=join_list(self.verses), audios_header="

Audios

" if len(self.audios) > 0 else "", audios_html=join_list(self.audios), latex_file=self.latex_file) def set_capo(self, capo): self.capo = capo def add_audio(self, audio): assert isinstance(audio, Audio) self.audios.append(audio) def add_verse(self, verse): assert isinstance(verse, Verse) self.verses.append(verse) def get_url(self): return "%03d %s" % (self.number, self.name.replace("¿", "").replace("?", "")) def chorded(self): for v in self.verses: if v.chorded(): return True return False class Verse: def __init__(self, is_chorus=False): self.is_chorus = is_chorus self.lines = [] def __str__(self): return """
%s
""" % ("chorus" if self.is_chorus else "verse", join_list([str(l) for l in self.lines], "\n
\n")) def add_line(self, line): assert isinstance(line, Line) self.lines.append(line) def chorded(self): for line in self.lines: if line.chorded(): return True return False class Line: ECHO_BEGIN = '' ECHO_END = '' def __init__(self, text, extras): self.text = text self.extras = extras self.chord_arr = [] self.lyric_arr = [] self.build() self.remove_brackets() def __str__(self): assert len(self.chord_arr) == len(self.lyric_arr) return join_list(["""%s
%s%s
""" % (self.chord_arr[i]["rowspan"] if "rowspan" in self.chord_arr[i] else 1, '' % self.chord_arr[i]["class"] if "class" in self.chord_arr[i] else "", self.chord_arr[i]["chord"] if "chord" in self.chord_arr[i] else "", '%s' % self.lyric_arr[i] if "rowspan" not in self.chord_arr[i] else '' ) for i in range(len(self.chord_arr))], separator='') def add_chord(self, index, chord): self.add_item(index, "chord", chord) def add_dir_rep(self, index, data): self.add_item(index, "dir-rep", data) def add_rep(self, index, data): self.add_item(index, "rep", data) def add_echo(self, index): self.add_item(index, "echo", None) def add_item(self, index, the_type, data): if index not in self.extras: self.extras[index] = [] self.extras[index].append({'type': the_type, 'data': data}) def build(self): self.chord_arr = [{}] self.lyric_arr = [""] inside_echo = False mid = True for i in range(len(self.text) + 1): for e in self.extras[i] if i in self.extras else []: if e["type"] == "chord": floating = (i < len(self.text) and self.text[i] == ' ' and (i == 0 or self.text[i - 1] == ' ')) or \ (i >= len(self.text) and self.text[i - 1] == ' ') self.chord_arr.append({'chord': e["data"]}) if inside_echo and self.lyric_arr[-1] != '': self.lyric_arr[-1] += Line.ECHO_END if floating: self.lyric_arr.append('') self.chord_arr.append({}) self.lyric_arr.append(Line.ECHO_BEGIN if inside_echo else '') mid = True elif e["type"] == "dir-rep": self.chord_arr.append({'class': e["data"], 'rowspan': 2}) self.lyric_arr.append('') if mid and inside_echo: self.lyric_arr[-1] += Line.ECHO_END mid = False elif e["type"] == "rep": self.lyric_arr.append('(x%d)' % (e["data"])) self.chord_arr.append({}) mid = False elif e["type"] == "echo": if not mid: self.lyric_arr.append(Line.ECHO_BEGIN if inside_echo else '') mid = True start = Line.ECHO_BEGIN + '(' end = ')' + Line.ECHO_END self.lyric_arr[-1] += end if inside_echo else start inside_echo = not inside_echo else: print("Unrecognized type", e["type"]) if i != len(self.text): if not mid: self.chord_arr.append({}) self.lyric_arr.append(Line.ECHO_BEGIN if inside_echo else '') mid = True self.lyric_arr[-1] += self.text[i] for i in range(len(self.lyric_arr)): self.lyric_arr[i] = re.sub(r"(^ | $)", " ", self.lyric_arr[i]) def remove_brackets(self): self.lyric_arr = [l.replace('}', '').replace('{', '') for l in self.lyric_arr] def chorded(self): for key in self.extras: for i in self.extras[key]: if i["type"] == "chord": return True return False class Chord: N_CHORDS = 12 CHORDS_LAT = ['Do', 'Do#', 'Re', 'Re#', 'Mi', 'Fa', 'Fa#', 'Sol', 'Sol#', 'La', 'Sib', 'Si'] CHORDS_ENG = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'B&', 'B'] ENG_INDEX = {'C': 0, 'C#': 1, 'D&': 1, 'D': 2, 'D#': 3, 'E&': 3, 'E': 4, 'F&': 4, 'F': 5, 'E#': 5, 'F#': 6, 'G&': 6, 'G': 7, 'G#': 8, 'A&': 8, 'A': 9, 'A#': 10, 'B&': 10, 'B': 11, 'C&': 11, 'B#': 0} def __init__(self, text, base_transpose=0): self.text = text self.chords = [] self.base_transpose = base_transpose ignore = False for i, char in enumerate(text): if ignore: ignore = False continue if "A" <= char <= "G": if len(text) > i + 1 and (text[i + 1] == "#" or text[i + 1] == "&"): self.chords.append({'text': char + text[i + 1], 'chord': True}) ignore = True else: self.chords.append({'text': char, 'chord': True}) else: self.chords.append({'text': char, 'chord': False}) def __str__(self): res = "" for c in self.chords: if c['chord']: res += "%s" % Chord.CHORDS_LAT[Chord.ENG_INDEX[c['text']]] else: res += c['text'] return res class Audio: def __init__(self, date, audio_file): assert isinstance(date, datetime) self.date = date self.date_text = date.strftime("%A %-d de %B del %Y") self.audio_file = audio_file def __str__(self): return """
Audio del %s Descargar
""" % (self.date_text, self.audio_file, self.audio_file)