mirror of https://github.com/patacrep/patacrep.git
Romain Goffe
12 years ago
6 changed files with 5 additions and 424 deletions
@ -1,282 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding: utf-8 -*- |
|
||||
# |
|
||||
|
|
||||
import getopt, sys |
|
||||
import os.path |
|
||||
import re |
|
||||
import json |
|
||||
import locale |
|
||||
import shutil |
|
||||
import locale |
|
||||
import platform |
|
||||
|
|
||||
from utils.utils import recursiveFind |
|
||||
|
|
||||
reTitle = re.compile('(?<=beginsong\\{)(.(?<!\\}]))+') |
|
||||
reArtist = re.compile('(?<=by=)(.(?<![,\\]\\}]))+') |
|
||||
reAlbum = re.compile('(?<=album=)(.(?<![,\\]\\}]))+') |
|
||||
|
|
||||
class Song: |
|
||||
def __init__(self, title, artist, album, path): |
|
||||
self.title = title |
|
||||
self.artist = artist |
|
||||
self.album = album |
|
||||
self.path = path |
|
||||
def __repr__(self): |
|
||||
return repr((self.title, self.artist, self.album, self.path)) |
|
||||
|
|
||||
if platform.system() == "Linux": |
|
||||
from xdg.BaseDirectory import * |
|
||||
cachePath = os.path.join(xdg_cache_home, 'songbook') |
|
||||
else: |
|
||||
cachePath = os.path.join('cache', 'songbook') |
|
||||
|
|
||||
def makeCoverCache(library): |
|
||||
''' |
|
||||
Copy all pictures found in the libraries into a unique cache |
|
||||
folder. |
|
||||
''' |
|
||||
# create the cache directory if it does not exist |
|
||||
if not os.path.exists(cachePath): |
|
||||
os.makedirs(cachePath) |
|
||||
|
|
||||
# copy pictures file into the cache directory |
|
||||
covers = recursiveFind(os.path.join(library, 'songs'), '*.jpg') |
|
||||
for cover in covers: |
|
||||
coverPath = os.path.join(cachePath, os.path.basename(cover)) |
|
||||
shutil.copy(cover, coverPath) |
|
||||
|
|
||||
def matchRegexp(reg, iterable): |
|
||||
return [ m.group(1) for m in (reg.match(l) for l in iterable) if m ] |
|
||||
|
|
||||
def unprefixed(title, prefixes): |
|
||||
"""Remove the first prefix of the list in the beginning of title (if any). |
|
||||
""" |
|
||||
for prefix in prefixes: |
|
||||
match = re.compile(r"^(%s)\b\s*(.*)$" % prefix).match(title) |
|
||||
if match: |
|
||||
return match.group(2) |
|
||||
return title |
|
||||
|
|
||||
def songslist(library, songs, prefixes): |
|
||||
song_objects = [] |
|
||||
for s in songs: |
|
||||
path = library + 'songs/' + s |
|
||||
with open(path, 'r+') as f: |
|
||||
data = f.read() |
|
||||
title = reTitle.search(data).group(0) |
|
||||
artist = reArtist.search(data.replace("{","")).group(0) |
|
||||
match = reAlbum.search(data.replace("{","")) |
|
||||
if match: |
|
||||
album = match.group(0) |
|
||||
else: |
|
||||
album = '' |
|
||||
song_objects.append(Song(title, artist, album, path)) |
|
||||
|
|
||||
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(unprefixed(x.title, prefixes))) |
|
||||
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(x.album)) |
|
||||
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(x.artist)) |
|
||||
|
|
||||
result = [ '\\input{{{0}}}'.format(song.path.replace("\\","/").strip()) for song in song_objects ] |
|
||||
return '\n'.join(result) |
|
||||
|
|
||||
def parseTemplate(template): |
|
||||
embeddedJsonPattern = re.compile(r"^%%:") |
|
||||
f = open(template) |
|
||||
code = [ line[3:-1] for line in f if embeddedJsonPattern.match(line) ] |
|
||||
f.close() |
|
||||
data = json.loads(''.join(code)) |
|
||||
parameters = dict() |
|
||||
for param in data: |
|
||||
parameters[param["name"]] = param |
|
||||
return parameters |
|
||||
|
|
||||
def toValue(parameter, data): |
|
||||
if "type" not in parameter: |
|
||||
return data |
|
||||
elif parameter["type"] == "stringlist": |
|
||||
if "join" in parameter: |
|
||||
joinText = parameter["join"] |
|
||||
else: |
|
||||
joinText = '' |
|
||||
return joinText.join(data) |
|
||||
elif parameter["type"] == "color": |
|
||||
return data[1:] |
|
||||
elif parameter["type"] == "font": |
|
||||
return data+'pt' |
|
||||
elif parameter["type"] == "enum": |
|
||||
return data |
|
||||
elif parameter["type"] == "file": |
|
||||
return data |
|
||||
elif parameter["type"] == "flag": |
|
||||
if "join" in parameter: |
|
||||
joinText = parameter["join"] |
|
||||
else: |
|
||||
joinText = '' |
|
||||
return joinText.join(data) |
|
||||
|
|
||||
def formatDeclaration(name, parameter): |
|
||||
value = "" |
|
||||
if "default" in parameter: |
|
||||
value = parameter["default"] |
|
||||
return '\\def\\set@{name}#1{{\\def\\get{name}{{#1}}}}\n'.format(name=name) + formatDefinition(name, toValue(parameter, value)) |
|
||||
|
|
||||
def formatDefinition(name, value): |
|
||||
return '\\set@{name}{{{value}}}\n'.format(name=name, value=value) |
|
||||
|
|
||||
def makeTexFile(sb, library, output): |
|
||||
name = output[:-4] |
|
||||
|
|
||||
# default value |
|
||||
template = "patacrep.tmpl" |
|
||||
songs = [] |
|
||||
titleprefixwords = "" |
|
||||
prefixes = [] |
|
||||
|
|
||||
# parse the songbook data |
|
||||
if "template" in sb: |
|
||||
template = sb["template"] |
|
||||
del sb["template"] |
|
||||
if "songs" in sb: |
|
||||
songs = sb["songs"] |
|
||||
del sb["songs"] |
|
||||
if "titleprefixwords" in sb: |
|
||||
prefixes = sb["titleprefixwords"] |
|
||||
for prefix in sb["titleprefixwords"]: |
|
||||
titleprefixwords += "\\titleprefixword{%s}\n" % prefix |
|
||||
sb["titleprefixwords"] = titleprefixwords |
|
||||
|
|
||||
parameters = parseTemplate("templates/"+template) |
|
||||
|
|
||||
# output relevant fields |
|
||||
out = open(output, 'w') |
|
||||
out.write('%% This file has been automatically generated, do not edit!\n') |
|
||||
out.write('\\makeatletter\n') |
|
||||
# output automatic parameters |
|
||||
out.write(formatDeclaration("name", {"default":name})) |
|
||||
out.write(formatDeclaration("songslist", {"type":"stringlist"})) |
|
||||
# output template parameter command |
|
||||
for name, parameter in parameters.iteritems(): |
|
||||
out.write(formatDeclaration(name, parameter)) |
|
||||
# output template parameter values |
|
||||
for name, value in sb.iteritems(): |
|
||||
if name in parameters: |
|
||||
out.write(formatDefinition(name, toValue(parameters[name],value))) |
|
||||
# output songslist |
|
||||
if songs == "all": |
|
||||
songs = map(lambda x: x[len(library) + 6:], recursiveFind(os.path.join(library, 'songs'), '*.sg')) |
|
||||
|
|
||||
if len(songs) > 0: |
|
||||
out.write(formatDefinition('songslist', songslist(library, songs, prefixes))) |
|
||||
out.write('\\makeatother\n') |
|
||||
|
|
||||
# output template |
|
||||
commentPattern = re.compile(r"^\s*%") |
|
||||
f = open("templates/"+template) |
|
||||
content = [ line for line in f if not commentPattern.match(line) ] |
|
||||
|
|
||||
for index, line in enumerate(content): |
|
||||
if re.compile("getCacheDirectory").search(line): |
|
||||
line = line.replace("\\getCacheDirectory", cachePath.replace("\\","/") + "/") |
|
||||
content[index] = line |
|
||||
if re.compile("getLibraryImgDirectory").search(line): |
|
||||
line = line.replace("\\getLibraryImgDirectory", library + "img/") |
|
||||
content[index] = line |
|
||||
if re.compile("getLibraryLilypondDirectory").search(line): |
|
||||
line = line.replace("\\getLibraryLilypondDirectory", library + "lilypond/") |
|
||||
content[index] = line |
|
||||
|
|
||||
f.close() |
|
||||
out.write(''.join(content)) |
|
||||
|
|
||||
out.close() |
|
||||
|
|
||||
def makeDepend(sb, library, output): |
|
||||
name = output[:-2] |
|
||||
|
|
||||
indexPattern = re.compile(r"^[^%]*\\(?:newauthor|new)index\{.*\}\{(.*?)\}") |
|
||||
lilypondPattern = re.compile(r"^[^%]*\\(?:lilypond)\{(.*?)\}") |
|
||||
|
|
||||
# check for deps (in sb data) |
|
||||
deps = []; |
|
||||
if sb["songs"] == "all": |
|
||||
deps += recursiveFind(os.path.join(library, 'songs'), '*.sg') |
|
||||
else: |
|
||||
deps += map(lambda x: library + "songs/" + x, sb["songs"]) |
|
||||
|
|
||||
# check for lilypond deps (in songs data) if necessary |
|
||||
lilypond = [] |
|
||||
if "bookoptions" in sb and "lilypond" in sb["bookoptions"]: |
|
||||
for filename in deps: |
|
||||
tmpl = open(filename) |
|
||||
lilypond += matchRegexp(lilypondPattern, tmpl) |
|
||||
tmpl.close() |
|
||||
|
|
||||
# check for index (in template file) |
|
||||
if "template" in sb: |
|
||||
filename = sb["template"] |
|
||||
else: |
|
||||
filename = "patacrep.tmpl" |
|
||||
tmpl = open("templates/"+filename) |
|
||||
idx = map(lambda x: x.replace("\getname", name), matchRegexp(indexPattern, tmpl)) |
|
||||
tmpl.close() |
|
||||
|
|
||||
# write .d file |
|
||||
out = open(output, 'w') |
|
||||
out.write('{0} {1} : {2}\n'.format(output, name+".tex", ' '.join(deps))) |
|
||||
out.write('{0} : {1}\n'.format(name+".pdf", ' '.join(map(lambda x: x+".sbx",idx)+map(lambda x: library+"lilypond/"+x+".pdf", lilypond)))) |
|
||||
out.write('\t$(LATEX) {0}\n'.format(name+".tex")) |
|
||||
out.write('{0} : {1}\n'.format(' '.join(map(lambda x: x+".sxd",idx)), name+".aux")) |
|
||||
out.close() |
|
||||
|
|
||||
def usage(): |
|
||||
print "No usage information yet." |
|
||||
|
|
||||
def main(): |
|
||||
locale.setlocale(locale.LC_ALL, '') # set script locale to match user's |
|
||||
try: |
|
||||
opts, args = getopt.getopt(sys.argv[1:], |
|
||||
"hs:o:l:d", |
|
||||
["help","songbook=","output=","depend","library="]) |
|
||||
except getopt.GetoptError, err: |
|
||||
# print help and exit |
|
||||
print str(err) |
|
||||
usage() |
|
||||
sys.exit(2) |
|
||||
|
|
||||
songbook = None |
|
||||
depend = False |
|
||||
output = None |
|
||||
library = './' |
|
||||
|
|
||||
for o, a in opts: |
|
||||
if o in ("-h", "--help"): |
|
||||
usage() |
|
||||
sys.exit() |
|
||||
elif o in ("-s", "--songbook"): |
|
||||
songbook = a |
|
||||
elif o in ("-d", "--depend"): |
|
||||
depend = True |
|
||||
elif o in ("-o", "--output"): |
|
||||
output = a |
|
||||
elif o in ("-l", "--library"): |
|
||||
if not a.endswith('/'): |
|
||||
a += '/' |
|
||||
library = a |
|
||||
else: |
|
||||
assert False, "unhandled option" |
|
||||
|
|
||||
makeCoverCache(library) |
|
||||
if songbook and output: |
|
||||
f = open(songbook) |
|
||||
sb = json.load(f) |
|
||||
f.close() |
|
||||
|
|
||||
if depend: |
|
||||
makeDepend(sb, library, output) |
|
||||
else: |
|
||||
makeTexFile(sb, library, output) |
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
main() |
|
@ -1,138 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
# -*- coding: utf-8 -*- |
|
||||
# Generate indexes files for the Crep's chordbook compilation. This is |
|
||||
# a replacement for the original makeindex program written in C that |
|
||||
# produces an index file (.sbx) from a file generated by the latex |
|
||||
# compilation of the songbook (.sxd). |
|
||||
# |
|
||||
# Usage : songbook-makeindex.py src |
|
||||
# src is the .sxd file generated by latex |
|
||||
# |
|
||||
|
|
||||
import os.path |
|
||||
import glob |
|
||||
import re |
|
||||
import sys |
|
||||
from optparse import OptionParser |
|
||||
import sortindex |
|
||||
import locale |
|
||||
|
|
||||
# Pattern set to ignore latex command in title prefix |
|
||||
keywordPattern = re.compile(r"^%(\w+)\s?(.*)$") |
|
||||
firstLetterPattern = re.compile(r"^(?:\{?\\\w+\}?)*[^\w]*(\w)") |
|
||||
|
|
||||
class index: |
|
||||
data = dict() |
|
||||
keywords = dict() |
|
||||
|
|
||||
def filter(self, key): |
|
||||
letter = firstLetterPattern.match(key).group(1) |
|
||||
if re.match('\d',letter): |
|
||||
letter = '0-9' |
|
||||
return (letter.upper(), key) |
|
||||
|
|
||||
def keyword(self, key, word): |
|
||||
if not self.keywords.has_key(key): |
|
||||
self.keywords[key] = [] |
|
||||
self.keywords[key].append(word) |
|
||||
|
|
||||
def compileKeywords(self): |
|
||||
self.prefix_patterns = [] |
|
||||
if 'prefix' in self.keywords: |
|
||||
for prefix in self.keywords['prefix']: |
|
||||
self.prefix_patterns.append(re.compile(r"^(%s)\b\s*(.*)$" % prefix)) |
|
||||
|
|
||||
def add(self, key, number, link): |
|
||||
for pattern in self.prefix_patterns: |
|
||||
match = pattern.match(key) |
|
||||
if match: |
|
||||
key = "%s (%s)" % (match.group(2), match.group(1)) |
|
||||
break # Only one match per key |
|
||||
(first, key) = self.filter(key) |
|
||||
if not self.data.has_key(first): |
|
||||
self.data[first] = dict() |
|
||||
if not self.data[first].has_key(key): |
|
||||
self.data[first][key] = [] |
|
||||
self.data[first][key].append({'num':number, 'link':link}) |
|
||||
|
|
||||
def refToStr(self, ref): |
|
||||
if sys.version_info >= (2,6): |
|
||||
return '\\hyperlink{{{0[link]}}}{{{0[num]}}}'.format(ref) |
|
||||
else: |
|
||||
return '\\hyperlink{%(link)s}{%(num)s}' % ref |
|
||||
|
|
||||
def entryToStr(self, key, entry): |
|
||||
if sys.version_info >= (2,6): |
|
||||
return '\\idxentry{{{0}}}{{{1}}}\n'.format(key, '\\\\'.join(map(self.refToStr, entry))) |
|
||||
else: |
|
||||
return '\\idxentry{%s}{%s}\n' % (key, '\\\\'.join(map(self.refToStr, entry))) |
|
||||
|
|
||||
def idxBlockToStr(self, letter, entries): |
|
||||
str = '\\begin{idxblock}{'+letter+'}'+'\n' |
|
||||
for key in sorted(entries.keys(), key=sortindex.sortkey): |
|
||||
str += self.entryToStr(key, entries[key]) |
|
||||
str += '\\end{idxblock}'+'\n' |
|
||||
return str |
|
||||
|
|
||||
def entriesToStr(self): |
|
||||
str = "" |
|
||||
for letter in sorted(self.data.keys()): |
|
||||
str += self.idxBlockToStr(letter, self.data[letter]) |
|
||||
return str |
|
||||
|
|
||||
def processSXDEntry(tab): |
|
||||
return (tab[0], tab[1], tab[2]) |
|
||||
|
|
||||
def processSXD(filename): |
|
||||
file = open(filename) |
|
||||
data = [] |
|
||||
for line in file: |
|
||||
data.append(line.strip()) |
|
||||
file.close() |
|
||||
|
|
||||
type = data[0] |
|
||||
i = 1 |
|
||||
idx = index() |
|
||||
|
|
||||
if len(data) > 1: |
|
||||
while data[i].startswith('%'): |
|
||||
keywords = keywordPattern.match(data[i]).groups() |
|
||||
idx.keyword(keywords[0],keywords[1]) |
|
||||
i += 1 |
|
||||
|
|
||||
idx.compileKeywords() |
|
||||
for i in range(i,len(data),3): |
|
||||
entry = processSXDEntry(data[i:i+3]) |
|
||||
idx.add(entry[0],entry[1],entry[2]) |
|
||||
return idx |
|
||||
|
|
||||
def usage(exitCode=None): |
|
||||
print "usage: songbook-makeindex.py [options] source" |
|
||||
sys.exit(exitCode) |
|
||||
|
|
||||
def main(): |
|
||||
locale.setlocale(locale.LC_ALL, '') |
|
||||
usage = "usage: %prog [options] FILE" |
|
||||
parser = OptionParser(usage) |
|
||||
parser.add_option("-o", "--output", dest="filename", |
|
||||
help="write result into FILE", metavar="FILE") |
|
||||
(options, args) = parser.parse_args() |
|
||||
|
|
||||
# Args processing |
|
||||
if len(args) != 1: |
|
||||
parser.error("incorrect number of arguments") |
|
||||
if not os.path.exists(args[0]): |
|
||||
parser.error("inexistant input file") |
|
||||
|
|
||||
# Options processing |
|
||||
if options.filename: |
|
||||
output = open(options.filename,"w") |
|
||||
else: |
|
||||
output = sys.stdout |
|
||||
|
|
||||
# Actual processing |
|
||||
idx = processSXD(args[0]) |
|
||||
output.write(idx.entriesToStr()) |
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
main() |
|
Loading…
Reference in new issue