mtx-flac.lua /size: 10 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
mtx-flac
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to mtxrun.lua
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
local
sub
,
match
,
byte
,
lower
=
string
.
sub
,
string
.
match
,
string
.
byte
,
string
.
lower
10
local
readstring
,
readnumber
=
io
.
readstring
,
io
.
readnumber
11
local
concat
,
sortedpairs
,
sort
,
keys
=
table
.
concat
,
table
.
sortedpairs
,
table
.
sort
,
table
.
keys
12
local
tonumber
=
tonumber
13
local
tobitstring
=
number
.
tobitstring
14
local
lpegmatch
=
lpeg
.
match
15
local
p_escaped
=
lpeg
.
patterns
.
xml
.
escaped
16 17
-- rather silly: pack info in bits while a flac file is large anyway
18 19
flac
=
flac
or
{
}
20 21
flac
.
report
=
string
.
format
22 23
local
splitter
=
lpeg
.
splitat
(
"
=
"
)
24
local
readers
=
{
}
25 26
readers
[
0
]
=
function
(
f
,
size
,
target
)
-- not yet ok .. todo: use bit32 lib
27
local
info
=
{
}
28
target
.
info
=
info
29
info
.
minimum_block_size
=
readnumber
(
f
,
-2
)
30
info
.
maximum_block_size
=
readnumber
(
f
,
-2
)
31
info
.
minimum_frame_size
=
readnumber
(
f
,
-3
)
32
info
.
maximum_frame_size
=
readnumber
(
f
,
-3
)
33
local
buffer
=
{
}
34
for
i
=
1
,
8
do
35
buffer
[
i
]
=
tobitstring
(
readnumber
(
f
,
1
)
)
36
end
37
local
bytes
=
concat
(
buffer
)
38
info
.
sample_rate_in_hz
=
tonumber
(
sub
(
bytes
,
1
,
20
)
,
2
)
-- 20
39
info
.
number_of_channels
=
tonumber
(
sub
(
bytes
,
21
,
23
)
,
2
)
-- 3
40
info
.
bits_per_sample
=
tonumber
(
sub
(
bytes
,
24
,
28
)
,
2
)
-- 5
41
info
.
samples_in_stream
=
tonumber
(
sub
(
bytes
,
29
,
64
)
,
2
)
-- 36
42
info
.
md5_signature
=
readstring
(
f
,
16
)
-- 128
43
end
44 45
readers
[
4
]
=
function
(
f
,
size
,
target
,
banner
)
46
local
tags
=
{
}
47
target
.
tags
=
tags
48
target
.
vendor
=
readstring
(
f
,
readnumber
(
f
,
-4
)
)
49
for
i
=
1
,
readnumber
(
f
,
-4
)
do
50
local
key
,
value
=
lpeg
.
match
(
splitter
,
readstring
(
f
,
readnumber
(
f
,
-4
)
)
)
51
tags
[
lower
(
key
)
]
=
value
52
end
53
end
54 55
readers
.
default
=
function
(
f
,
size
,
target
)
56
f
:
seek
(
"
cur
"
,
size
)
57
end
58 59
local
valid
=
{
60
[
"
fLaC
"
]
=
true
,
61
[
"
ID3♥
"
]
=
false
,
62
}
63 64
function
flac
.
getmetadata
(
filename
)
65
local
f
=
io
.
open
(
filename
,
"
rb
"
)
66
if
f
then
67
local
banner
=
readstring
(
f
,
4
)
68
local
whatsit
=
valid
[
banner
]
69
if
whatsit
~
=
nil
then
70
if
whatsit
=
=
false
then
71
flac
.
report
(
"
suspicious flac file: %s (%s)
"
,
filename
,
banner
)
72
end
73
local
data
=
{
74
banner
=
banner
,
75
filename
=
filename
,
76
filesize
=
lfs
.
attributes
(
filename
,
"
size
"
)
,
77
}
78
while
true
do
79
local
flag
=
readnumber
(
f
,
1
)
80
local
size
=
readnumber
(
f
,
3
)
81
local
last
=
flag
>
127
82
if
last
then
83
flag
=
flag
-
128
84
end
85
local
reader
=
readers
[
flag
]
or
readers
.
default
86
reader
(
f
,
size
,
data
,
banner
)
87
if
last
then
88
f
:
close
(
)
89
return
data
90
end
91
end
92
else
93
flac
.
report
(
"
no flac file: %s (%s)
"
,
filename
,
banner
)
94
end
95
f
:
close
(
)
96
else
97
flac
.
report
(
"
no file: %s
"
,
filename
)
98
end
99
end
100 101
function
flac
.
savecollection
(
pattern
,
filename
)
102
pattern
=
(
pattern
~
=
"
"
and
pattern
)
or
"
**/*.flac
"
103
filename
=
(
filename
~
=
"
"
and
filename
)
or
"
music-collection.xml
"
104
flac
.
report
(
"
identifying files using pattern %q
"
,
pattern
)
105
local
files
=
dir
.
glob
(
pattern
)
106
flac
.
report
(
"
%s files found, analyzing files
"
,
#
files
)
107
local
music
=
{
}
108
sort
(
files
)
109
for
i
=
1
,
#
files
do
110
local
name
=
files
[
i
]
111
local
data
=
flac
.
getmetadata
(
name
)
112
if
data
then
113
local
tags
=
data
.
tags
114
local
info
=
data
.
info
115
if
tags
and
info
then
116
local
artist
=
tags
.
artist
or
"
no-artist
"
117
local
album
=
tags
.
album
or
"
no-album
"
118
local
albums
=
music
[
artist
]
119
if
not
albums
then
120
albums
=
{
}
121
music
[
artist
]
=
albums
122
end
123
local
albumx
=
albums
[
album
]
124
if
not
albumx
then
125
albumx
=
{
126
year
=
tags
.
date
,
127
tracks
=
{
}
,
128
}
129
albums
[
album
]
=
albumx
130
end
131
albumx
.
tracks
[
tonumber
(
tags
.
tracknumber
)
or
0
]
=
{
132
title
=
tags
.
title
,
133
length
=
math
.
round
(
(
info
.
samples_in_stream
/
info
.
sample_rate_in_hz
)
)
,
134
}
135
else
136
flac
.
report
(
"
unable to read file
"
,
name
)
137
end
138
end
139
end
140
--
141
local
nofartists
=
0
142
local
nofalbums
=
0
143
local
noftracks
=
0
144
local
noferrors
=
0
145
--
146
local
allalbums
147
local
function
compare
(
a
,
b
)
148
local
ya
=
allalbums
[
a
]
.
year
or
0
149
local
yb
=
allalbums
[
b
]
.
year
or
0
150
if
ya
=
=
yb
then
151
return
a
<
b
152
else
153
return
ya
<
yb
154
end
155
end
156
local
function
getlist
(
albums
)
157
allalbums
=
albums
158
local
list
=
keys
(
albums
)
159
sort
(
list
,
compare
)
160
return
list
161
end
162
--
163
filename
=
file
.
addsuffix
(
filename
,
"
xml
"
)
164
local
f
=
io
.
open
(
filename
,
"
wb
"
)
165
if
f
then
166
flac
.
report
(
"
saving data in file %q
"
,
filename
)
167
f
:
write
(
"
<?xml version='1.0' standalone='yes'?>\n\n
"
)
168
f
:
write
(
"
<collection>\n
"
)
169
for
artist
,
albums
in
sortedpairs
(
music
)
do
170
nofartists
=
nofartists
+
1
171
f
:
write
(
"
\t<artist>\n
"
)
172
f
:
write
(
"
\t\t<name>
"
,
lpegmatch
(
p_escaped
,
artist
)
,
"
</name>\n
"
)
173
f
:
write
(
"
\t\t<albums>\n
"
)
174
local
list
=
getlist
(
albums
)
175
nofalbums
=
nofalbums
+
#
list
176
for
nofalbums
=
1
,
#
list
do
177
local
album
=
list
[
nofalbums
]
178
local
data
=
albums
[
album
]
179
f
:
write
(
"
\t\t\t<album year='
"
,
data
.
year
or
0
,
"
'>\n
"
)
180
f
:
write
(
"
\t\t\t\t<name>
"
,
lpegmatch
(
p_escaped
,
album
)
,
"
</name>\n
"
)
181
f
:
write
(
"
\t\t\t\t<tracks>\n
"
)
182
local
tracks
=
data
.
tracks
183
for
i
=
1
,
#
tracks
do
184
local
track
=
tracks
[
i
]
185
if
track
then
186
noftracks
=
noftracks
+
1
187
f
:
write
(
"
\t\t\t\t\t<track length='
"
,
track
.
length
,
"
'>
"
,
lpegmatch
(
p_escaped
,
track
.
title
)
,
"
</track>\n
"
)
188
else
189
noferrors
=
noferrors
+
1
190
flac
.
report
(
"
error in album: %q of %q, no track %s
"
,
album
,
artist
,
i
)
191
f
:
write
(
"
\t\t\t\t\t<error track='
"
,
i
,
"
'/>\n
"
)
192
end
193
end
194
f
:
write
(
"
\t\t\t\t</tracks>\n
"
)
195
f
:
write
(
"
\t\t\t</album>\n
"
)
196
end
197
f
:
write
(
"
\t\t</albums>\n
"
)
198
f
:
write
(
"
\t</artist>\n
"
)
199
end
200
f
:
write
(
"
</collection>\n
"
)
201
f
:
close
(
)
202
flac
.
report
(
"
%s tracks of %s albums of %s artists saved in %q (%s errors)
"
,
noftracks
,
nofalbums
,
nofartists
,
filename
,
noferrors
)
203
-- a secret option for alan braslau
204
if
environment
.
argument
(
"
bibtex
"
)
then
205
filename
=
file
.
replacesuffix
(
filename
,
"
bib
"
)
206
local
f
=
io
.
open
(
filename
,
"
wb
"
)
207
if
f
then
208
local
n
=
0
209
for
artist
,
albums
in
sortedpairs
(
music
)
do
210
local
list
=
getlist
(
albums
)
211
for
nofalbums
=
1
,
#
list
do
212
n
=
n
+
1
213
local
album
=
list
[
nofalbums
]
214
local
data
=
albums
[
album
]
215
local
tracks
=
data
.
tracks
216
f
:
write
(
"
@cd{entry-
"
,
n
,
"
,\n
"
)
217
f
:
write
(
"
\tartist = {
"
,
artist
,
"
},\n
"
)
218
f
:
write
(
"
\ttitle = {
"
,
album
or
"
no title
"
,
"
},\n
"
)
219
f
:
write
(
"
\tyear = {
"
,
data
.
year
or
0
,
"
},\n
"
)
220
f
:
write
(
"
\ttracks = {
"
,
#
tracks
,
"
},\n
"
)
221
for
i
=
1
,
#
tracks
do
222
local
track
=
tracks
[
i
]
223
if
track
then
224
noftracks
=
noftracks
+
1
225
f
:
write
(
"
\ttrack:
"
,
i
,
"
= {
"
,
track
.
title
,
"
},\n
"
)
226
f
:
write
(
"
\tlength:
"
,
i
,
"
= {
"
,
track
.
length
,
"
},\n
"
)
227
end
228
end
229
f
:
write
(
"
}\n
"
)
230
end
231
end
232
f
:
close
(
)
233
flac
.
report
(
"
additional bibtex file generated: %s
"
,
filename
)
234
end
235
end
236
--
237
else
238
flac
.
report
(
"
unable to save data in file %q
"
,
filename
)
239
end
240
end
241 242
--
243 244
local
helpinfo
=
[[
245<?xml version="1.0"?> 246<application> 247 <metadata> 248 <entry name="name">mtx-flac</entry> 249 <entry name="detail">ConTeXt Flac Helpers</entry> 250 <entry name="version">0.10</entry> 251 </metadata> 252 <flags> 253 <category name="basic"> 254 <subcategory> 255 <flag name="collect"><short>collect albums in xml file</short></flag> 256 <flag name="pattern"><short>use pattern for locating files</short></flag> 257 </subcategory> 258 </category> 259 </flags> 260 <examples> 261 <category> 262 <title>Example</title> 263 <subcategory> 264 <example><command>mtxrun --script flac --collect somename.flac</command></example> 265 <example><command>mtxrun --script flac --collect --pattern="m:/music/**")</command></example> 266 </subcategory> 267 </category> 268 </examples> 269</application> 270
]]
271 272
local
application
=
logs
.
application
{
273
name
=
"
mtx-flac
"
,
274
banner
=
"
ConTeXt Flac Helpers 0.10
"
,
275
helpinfo
=
helpinfo
,
276
}
277 278
flac
.
report
=
application
.
report
279 280
-- script code
281 282
scripts
=
scripts
or
{
}
283
scripts
.
flac
=
scripts
.
flac
or
{
}
284 285
function
scripts
.
flac
.
collect
(
)
286
local
files
=
environment
.
files
287
local
pattern
=
environment
.
arguments
.
pattern
288
if
#
files
>
0
then
289
for
i
=
1
,
#
files
do
290
local
filename
=
files
[
1
]
291
if
file
.
suffix
(
filename
)
=
=
"
flac
"
then
292
flac
.
savecollection
(
filename
,
file
.
replacesuffix
(
filename
,
"
xml
"
)
)
293
elseif
lfs
.
isdir
(
filename
)
then
294
local
pattern
=
filename
.
.
"
/**.flac
"
295
flac
.
savecollection
(
pattern
,
file
.
addsuffix
(
file
.
basename
(
filename
)
,
"
xml
"
)
)
296
else
297
flac
.
savecollection
(
file
.
replacesuffix
(
filename
,
"
flac
"
)
,
file
.
replacesuffix
(
filename
,
"
xml
"
)
)
298
end
299
end
300
elseif
pattern
then
301
flac
.
savecollection
(
file
.
addsuffix
(
pattern
,
"
flac
"
)
,
"
music-collection.xml
"
)
302
else
303
flac
.
report
(
"
no file(s) or pattern given
"
)
304
end
305
end
306 307
if
environment
.
argument
(
"
collect
"
)
then
308
scripts
.
flac
.
collect
(
)
309
elseif
environment
.
argument
(
"
exporthelp
"
)
then
310
application
.
export
(
environment
.
argument
(
"
exporthelp
"
)
,
environment
.
files
[
1
]
)
311
else
312
application
.
help
(
)
313
end
314