1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
4 from copy import deepcopy
10 from librarian import OutputFile, get_resource
11 from librarian.html import raw_printable_text
12 from .html import DaisyHtmlBuilder
15 def get_duration(path):
43 return "%02d:%02d:%02.3f" % (hours, minutes, seconds)
46 def populate(element, context):
48 element.text = element.text.format(**context)
50 element.tail = element.tail.format(**context)
51 for k, v in element.attrib.items():
52 element.attrib[k] = v.format(**context)
54 populate(child, context)
58 file_extension = 'daisy.zip'
60 def build(self, document, mp3, split_on=None, **kwargs):
62 raise ValueError("Need MP3 files")
64 outfile = tempfile.NamedTemporaryFile(delete=False)
65 zipf = zipfile.ZipFile(outfile, 'w')
67 directory = document.meta.url.slug + '/'
77 newdoc = deepcopy(document)
78 newdoc.tree.getroot().document = newdoc
80 master = newdoc.tree.getroot()[-1]
82 for item in list(master):
83 if item.tag == split_on:
87 headers.append(raw_printable_text(item))
88 if i != n and not (n == 1 and not i):
93 documents.append(newdoc)
95 documents = [document]
96 headers = [document.meta.title]
98 assert len(documents) == len(mp3)
100 narrator = mutagen.File(mp3[0]).get('TPE1')
101 narrator = narrator.text[0] if narrator else ''
104 for i, part in enumerate(documents):
106 html = DaisyHtmlBuilder().build(part)
109 directory + 'book%d.html' % i,
112 durations.append(get_duration(mp3[i]))
115 directory + "book%d.mp3" % i,
118 populate(tree.getroot(), context)
122 directory + 'content%d.smil' % i,
125 for fname in ('smil10.dtd', 'xhtml1-transitional.dtd', 'xhtml-lat1.ent', 'xhtml-special.ent', 'xhtml-symbol.ent'):
127 get_resource('res/daisy/' + fname),
130 duration = sum(durations)
131 hms = format_hms(duration)
135 "HHMMSS": hms.split('.')[0],
136 "Sd": "%.1f" % duration,
137 "TITLE": document.meta.title,
138 "PUBLISHER": document.meta.publisher[0],
139 "YEAR": document.meta.created_at[:4],
140 "MONTH": document.meta.created_at[5:7],
141 "AUTHOR": document.meta.author.readable(),
142 "NARRATOR": narrator,
145 tree = etree.parse(get_resource('res/daisy/er_book_info.xml'))
146 cont = tree.getroot()[0]
147 for i, dur in enumerate(durations):
148 etree.SubElement(cont, 'smil', nr=str(i+1), Name="content%i.smil" % i, dur="%.1f" % dur)
150 directory + 'er_book_info.xml',
151 etree.tostring(tree, xml_declaration=True))
153 tree = etree.parse(get_resource('res/daisy/master.smil'))
154 populate(tree.getroot(), context)
155 cont = tree.getroot()[-1]
156 for i, header in enumerate(headers):
157 etree.SubElement(cont, 'ref', title=header, src="content%d.smil#seq000001" % i, id="smil_%04d" % i)
159 directory + 'master.smil',
160 etree.tostring(tree, xml_declaration=True))
162 tree = etree.parse(get_resource('res/daisy/ncc.html'))
163 populate(tree.getroot(), context)
164 cont = tree.getroot()[-1]
165 for i, header in enumerate(headers):
167 h1 = etree.SubElement(
168 cont, 'h1', id='content', **{"class": "title"})
170 h1, "a", href='content%d.smil#par000001' % i).text = document.meta.title
172 h2 = etree.SubElement(
173 cont, 'h2', id='content', **{"class": "chapter"})
175 h2, "a", href='content%d.smil#par000001' % i).text = header
178 directory + 'ncc.html',
179 etree.tostring(tree, xml_declaration=True))
182 return OutputFile.from_filename(outfile.name)