| a18bfc9 by Aurélien Bompard at 2010-04-11 |
1 |
#!/usr/bin/env python |
| d3eca04 by Aurélien Bompard at 2010-04-12 |
2 |
# vim: set fileencoding=utf-8 tabstop=4 shiftwidth=4 expandtab smartindent: |
| f5600b8 by Aurélien Bompard at 2010-05-12 |
3 |
u""" |
| 7581434 by Aurélien Bompard at 2011-04-03 |
4 |
|
|
5 |
Make songs list |
|
6 |
--------------- |
|
7 |
|
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
8 |
Create a PDF file with the list of Karaoke songs available to Performous. |
|
9 |
|
|
10 |
Performous: http://performous.org/ |
|
11 |
|
| 7581434 by Aurélien Bompard at 2011-04-03 |
12 |
.. :Authors: |
|
13 |
Aurélien Bompard <aurelien@bompard.org> <http://aurelien.bompard.org> |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
14 |
|
| 7581434 by Aurélien Bompard at 2011-04-03 |
15 |
.. :License: |
|
16 |
GNU GPL v3 or later |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
17 |
""" |
|
18 |
|
|
19 |
import os |
|
20 |
import sys |
|
21 |
import tempfile |
|
22 |
import optparse |
|
23 |
import atexit |
| 8255535 by Aurélien Bompard at 2010-06-28 |
24 |
import re |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
25 |
from pprint import pprint |
|
26 |
|
| 8255535 by Aurélien Bompard at 2010-06-28 |
27 |
import PIL |
|
28 |
|
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
29 |
from reportlab.platypus import BaseDocTemplate, Paragraph, Spacer, Image, Table, TableStyle, Frame, PageTemplate |
|
30 |
from reportlab.lib.styles import ParagraphStyle |
|
31 |
from reportlab.lib import colors |
|
32 |
from reportlab.rl_config import defaultPageSize |
|
33 |
from reportlab.lib.units import inch |
|
34 |
|
|
35 |
|
|
36 |
defaultPageSize = (defaultPageSize[1], defaultPageSize[0]) |
|
37 |
PAGE_HEIGHT=defaultPageSize[1] |
|
38 |
PAGE_WIDTH=defaultPageSize[0] |
| a5106af by Aurélien Bompard at 2010-07-17 |
39 |
MARGIN = 8 |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
40 |
COLSEP = 5 |
|
41 |
|
|
42 |
songs = [] |
|
43 |
|
| 8255535 by Aurélien Bompard at 2010-06-28 |
44 |
def get_songs(cover_size, dpi): |
| a5106af by Aurélien Bompard at 2010-07-17 |
45 |
if opts.input: |
|
46 |
available_songs = scan_file() |
|
47 |
else: |
|
48 |
available_songs = scan_folders() |
|
49 |
for index, available_song in enumerate(available_songs): |
|
50 |
song = process_file(available_song, cover_size, dpi) |
|
51 |
if song: |
|
52 |
songs.append(song) |
|
53 |
sys.stdout.write("\rAdding song %d/%d" |
|
54 |
% (index+1, len(available_songs))) |
|
55 |
sys.stdout.flush() |
|
56 |
print |
|
57 |
print "Building PDF..." |
|
58 |
songs.sort(cmp=sort_songs) |
|
59 |
|
|
60 |
def sort_songs(x, y): |
|
61 |
key_x = x["artist"] |
|
62 |
key_y = y["artist"] |
|
63 |
for ignore in ["The ", "Les "]: |
|
64 |
if key_x.startswith(ignore): |
|
65 |
key_x = key_x[len(ignore):] |
|
66 |
if key_y.startswith(ignore): |
|
67 |
key_y = key_y[len(ignore):] |
|
68 |
if key_x == key_y: |
|
69 |
key_x = x["title"] |
|
70 |
key_y = y["title"] |
|
71 |
return cmp(key_x, key_y) |
|
72 |
|
|
73 |
def scan_folders(): |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
74 |
songs_dirs = ["/usr/share/performous/songs", |
|
75 |
"/usr/local/share/games/ultrastar/songs", |
|
76 |
"/usr/local/share/games/fretsonfire/data/songs", |
|
77 |
"/usr/share/games/ultrastar/songs", |
|
78 |
"/usr/share/games/fretsonfire/data/songs", |
|
79 |
"~/.ultrastar/songs", |
|
80 |
"~/.fretsonfire/songs", |
|
81 |
] |
| 8255535 by Aurélien Bompard at 2010-06-28 |
82 |
available_songs = [] |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
83 |
for songs_dir in songs_dirs: |
|
84 |
songs_dir = os.path.expanduser(songs_dir) |
|
85 |
if not os.path.exists(songs_dir): |
|
86 |
continue |
|
87 |
for root, dirs, files in os.walk(songs_dir): |
|
88 |
for cur_file in files: |
|
89 |
if not cur_file.lower().endswith(".txt"): |
|
90 |
continue |
| 8255535 by Aurélien Bompard at 2010-06-28 |
91 |
available_songs.append(os.path.join(root, cur_file)) |
| a5106af by Aurélien Bompard at 2010-07-17 |
92 |
return available_songs |
|
93 |
|
|
94 |
def scan_file(): |
|
95 |
available_songs = [] |
|
96 |
songs_file = open(opts.input, "r") |
|
97 |
for line in songs_file: |
|
98 |
song_file = line.strip() |
|
99 |
if not song_file.lower().endswith(".txt"): |
|
100 |
continue |
|
101 |
if not os.path.exists(song_file): |
|
102 |
print >>sys.stderr, "WARNING: file %s in the input file " \ |
|
103 |
% song_file + "cannot be found !" |
|
104 |
continue |
|
105 |
available_songs.append(song_file) |
|
106 |
songs_file.close() |
|
107 |
return available_songs |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
108 |
|
| 8255535 by Aurélien Bompard at 2010-06-28 |
109 |
def process_file(filepath, cover_size, dpi): |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
110 |
song = {} |
|
111 |
file_h = open(filepath, "r") |
|
112 |
for line in file_h: |
| b9913f6 by Aurélien Bompard at 2010-09-12 |
113 |
for tag in ["artist", "title", "cover", "background", "video"]: |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
114 |
if line.startswith("#%s:" % tag.upper()): |
|
115 |
song[tag] = ":".join(line.split(":")[1:]).strip() |
|
116 |
file_h.close() |
| 95b8bf4 by Aurélien Bompard at 2010-04-12 |
117 |
if "title" not in song: |
|
118 |
song["title"] = "" |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
119 |
for tag in ["cover", "background"]: |
|
120 |
if tag not in song: |
|
121 |
continue |
| 8255535 by Aurélien Bompard at 2010-06-28 |
122 |
song["image"] = os.path.realpath(os.path.join( |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
123 |
os.path.dirname(filepath), song[tag])) |
| 8255535 by Aurélien Bompard at 2010-06-28 |
124 |
if not os.path.exists(song["image"]): |
|
125 |
del song["image"] |
|
126 |
continue |
|
127 |
# Resize the image |
|
128 |
tmpimg, tmpimg_path = tempfile.mkstemp( |
|
129 |
prefix="mksonglist-", suffix=".jpg") |
|
130 |
img = PIL.Image.open(song["image"]) |
|
131 |
img.thumbnail((cover_size * dpi, cover_size * dpi), PIL.Image.ANTIALIAS) |
|
132 |
img.save(os.fdopen(tmpimg, "w"), "JPEG") |
|
133 |
atexit.register(os.remove, tmpimg_path) |
|
134 |
song["image"] = tmpimg_path |
|
135 |
break |
| b9913f6 by Aurélien Bompard at 2010-09-12 |
136 |
if "video" in song: |
|
137 |
videofile = os.path.join(os.path.dirname(filepath), song["video"]) |
|
138 |
if not os.path.exists(videofile): |
|
139 |
del song["video"] |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
140 |
return song |
|
141 |
|
| f0326ee by Aurélien Bompard at 2010-06-09 |
142 |
def filter_songs(karaoke): |
| 8255535 by Aurélien Bompard at 2010-06-28 |
143 |
filter_re = re.compile("(.*)\s+\(.*\)") |
| f0326ee by Aurélien Bompard at 2010-06-09 |
144 |
for song in songs[:]: |
| 8255535 by Aurélien Bompard at 2010-06-28 |
145 |
title = song["title"] |
|
146 |
if not karaoke and title.count("(Karaoke)"): |
| f0326ee by Aurélien Bompard at 2010-06-09 |
147 |
songs.remove(song) |
| 8255535 by Aurélien Bompard at 2010-06-28 |
148 |
continue |
|
149 |
filter_match = filter_re.match(title) |
|
150 |
if filter_match: |
|
151 |
short_title = filter_match.group(1) |
|
152 |
if short_title in [ s["title"] for s in songs ]: |
|
153 |
songs.remove(song) |
|
154 |
continue |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
155 |
|
|
156 |
def make_pdf(output, title, cover_size): |
|
157 |
doc = BaseDocTemplate( |
|
158 |
output, title=title, |
|
159 |
pagesize=defaultPageSize, |
|
160 |
) |
|
161 |
col_width = (PAGE_WIDTH - 2 * MARGIN - 2 * COLSEP) / 3 |
|
162 |
col_height = PAGE_HEIGHT - 2 * MARGIN |
| a5106af by Aurélien Bompard at 2010-07-17 |
163 |
vspace_left = col_height % (cover_size + 2) # used to bottom-align the 2nd and 3rd columns |
|
164 |
vspace_left_left = (col_height - 2*cover_size) % (cover_size + 2) # used to bottom-align the left column |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
165 |
frame_left = Frame(MARGIN, MARGIN, col_width, col_height, |
| a5106af by Aurélien Bompard at 2010-07-17 |
166 |
leftPadding=0, rightPadding=0, |
|
167 |
topPadding=vspace_left_left + 2*cover_size, |
|
168 |
bottomPadding=0, id="left", showBoundary=0) |
| 81a305c by Aurélien Bompard at 2010-06-09 |
169 |
frame_center = Frame(MARGIN + col_width + COLSEP, MARGIN, |
|
170 |
col_width, col_height, leftPadding=0, |
|
171 |
rightPadding=0, topPadding=vspace_left, |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
172 |
bottomPadding=0, id="center") |
| 81a305c by Aurélien Bompard at 2010-06-09 |
173 |
frame_right = Frame(PAGE_WIDTH - MARGIN - col_width, MARGIN, |
|
174 |
col_width, col_height, leftPadding=0, |
|
175 |
rightPadding=0, topPadding=vspace_left, |
|
176 |
bottomPadding=0, id="left") |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
177 |
doc.addPageTemplates(PageTemplate("threecols", frames=[frame_left, frame_center, frame_right])) |
|
178 |
Story = [] |
|
179 |
text_style = ParagraphStyle(name="labels", fontSize=12) |
|
180 |
table_style = TableStyle([ |
|
181 |
("VALIGN", (0,0), (-1,-1), "MIDDLE"), |
|
182 |
#("LEADING", (0,0), (-1,-1), 1), |
|
183 |
("TOPPADDING", (0,0), (-1,-1), 1), |
|
184 |
("BOTTOMPADDING", (0,0), (-1,-1), 1), |
|
185 |
("LEFTPADDING", (0,0), (0,-1), 0), |
|
186 |
("RIGHTPADDING", (0,0), (-1,-1), 0), |
|
187 |
("LEFTPADDING", (1,0), (1,-1), 5), |
|
188 |
#("BOX", (0,0), (-1,-1), 0.25, colors.gray), |
|
189 |
]) |
|
190 |
table_contents = [] |
|
191 |
for song in songs: |
| 8255535 by Aurélien Bompard at 2010-06-28 |
192 |
try: |
|
193 |
image = None |
| a5106af by Aurélien Bompard at 2010-07-17 |
194 |
text = "%s - %s" % (song["artist"], song["title"]) |
|
195 |
text = text.replace("&", "&") |
| b9913f6 by Aurélien Bompard at 2010-09-12 |
196 |
if "video" in song: |
|
197 |
text = "<i>%s</i>" % text |
| a5106af by Aurélien Bompard at 2010-07-17 |
198 |
label = Paragraph(text, text_style) |
| 8255535 by Aurélien Bompard at 2010-06-28 |
199 |
if "image" in song: |
|
200 |
image = Image(song["image"], cover_size, cover_size) |
|
201 |
#image.drawHeight = cover_size *I.drawHeight / I.drawWidth |
|
202 |
#image.drawWidth = cover_size |
|
203 |
else: |
|
204 |
image = Spacer(1, cover_size) |
|
205 |
row = [image, label] |
|
206 |
table_contents.append(row) |
|
207 |
except Exception: |
|
208 |
print >>sys.stderr, "Error when printing %s" % repr(song) |
|
209 |
raise |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
210 |
Story.append(Table(table_contents, |
|
211 |
colWidths=(cover_size + 2, col_width - (cover_size + 2)), |
|
212 |
style=table_style)) |
|
213 |
doc.build(Story) |
|
214 |
|
|
215 |
def parse_opts(): |
|
216 |
#usage = "usage: %prog [options]" |
|
217 |
parser = optparse.OptionParser() |
|
218 |
parser.add_option("-o", "--output", dest="output", default="songs.pdf", |
|
219 |
help="Output file name (default: %default)") |
|
220 |
parser.add_option("-t", "--title", dest="title", default="Songs", |
|
221 |
help="PDF title (default: %default)") |
|
222 |
parser.add_option("-k", "--karaoke", dest="karaoke", action="store_true", |
|
223 |
help="List the 'karaoke' versions (default: False)") |
| 6fdcac4 by Aurélien Bompard at 2010-06-09 |
224 |
parser.add_option("-c", "--cover-size", dest="cover_size", default=32, |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
225 |
type="int", help="Cover size (default: %default pt)") |
| 8255535 by Aurélien Bompard at 2010-06-28 |
226 |
parser.add_option("--dpi", dest="dpi", default=96, |
|
227 |
help="Image resolution in Dots Per Inch (default: %default)") |
| a5106af by Aurélien Bompard at 2010-07-17 |
228 |
parser.add_option("-i", "--input", dest="input", metavar="FILE", |
|
229 |
help="Read the song list from this file instead of " |
|
230 |
"scanning the folders") |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
231 |
opts, args = parser.parse_args() |
|
232 |
if len(args) != 0: |
|
233 |
parser.error("No arguments allowed") |
| a5106af by Aurélien Bompard at 2010-07-17 |
234 |
if opts.input and not os.path.exists(opts.input): |
|
235 |
parser.error("The file %s does not exist" % opts.input) |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
236 |
return opts |
|
237 |
|
|
238 |
if __name__ == '__main__': |
|
239 |
opts = parse_opts() |
| 8255535 by Aurélien Bompard at 2010-06-28 |
240 |
get_songs(opts.cover_size, opts.dpi) |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
241 |
#pprint(songs) |
| f0326ee by Aurélien Bompard at 2010-06-09 |
242 |
filter_songs(opts.karaoke) |
| a18bfc9 by Aurélien Bompard at 2010-04-11 |
243 |
make_pdf(opts.output, opts.title, opts.cover_size) |
|
244 |
print "PDF written to", opts.output |