1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
module Ids = SkhcDb.Ids.Map
module Strings = Map.Make (String)
module Roles = SkhcDb.Roles
module Metadata = SkhcMetadata.Metadata
let names s = String.concat " | " s
let participants' participants =
let a, b, c =
Ids.fold
(fun _ (name, roles) ->
Roles.fold
(fun role ((artist_names, album_artist, compositors) as aac) ->
match role with
| SkhcDb.Role.Artist ->
(name :: artist_names, album_artist, compositors)
| AlbumArtist ->
(artist_names, name :: album_artist, compositors)
| Composer ->
(artist_names, album_artist, name :: compositors)
| Unknown ->
aac
)
roles
)
participants ([], [], [])
in
(List.rev a, List.rev b, List.rev c)
let substitute_f ?metadata m format =
let format =
match metadata with
| None ->
format
| Some metadata ->
SkhcQuery.Format.map
(function
| String (Arbitrary s) ->
let s = String.uppercase_ascii s in
let s =
metadata
|> SkhcMetadata.Metadata.property_list s
|> String.concat " | "
in
Text s
| s ->
s
)
format
in
SkhcQuery.Format.(format |> substitute m |> to_string)
let substitute_safe_f ?metadata map format =
let map =
List.map
(fun (key, value) ->
let value = SkhcUtil.StringUtil.make_path_safe value in
(key, value)
)
map
in
substitute_f ?metadata map format
let filename_f ~extension config metadata format =
let title =
let default = SkhcConfig.Config.default_title config in
Option.value ~default @@ Metadata.title metadata
in
let artist_names =
match Metadata.artists metadata with
| [] ->
SkhcConfig.Config.default_artist config
| _ :: _ as a ->
names a
in
let album_artists = Metadata.album_artists metadata in
let album =
let default = config |> SkhcConfig.Config.default_album in
Option.value ~default @@ Metadata.album metadata
in
let composers = Metadata.composers metadata in
let title_sort = Metadata.title_sort metadata in
let year = Metadata.year metadata in
let track = Metadata.track metadata in
let disc = Metadata.disc metadata in
let genre = Option.value ~default:String.empty @@ Metadata.genre metadata in
let some_f = Printf.sprintf "%02d" in
let zero = "0" in
let map =
SkhcQuery.Format.Node.
[
(String Title, title);
(String SortName, Option.value ~default:String.empty title_sort);
(String Artist, artist_names);
(String AlbumArtist, names album_artists);
(String Composer, names composers);
(String Album, album);
(String Genre, genre);
(String FileExtension, extension);
(Int Track, Option.fold ~some:some_f ~none:zero track);
(Int Year, Option.fold ~some:some_f ~none:zero year);
(Int Disc, Option.fold ~some:some_f ~none:zero disc);
]
in
substitute_safe_f map format
let filename ~extension config metadata format =
filename_f ~extension config metadata (SkhcQuery.Format.of_string format)
let music_f music album artist_roles properties format =
let artist_names, album_artists, composers = participants' artist_roles in
let metadata = Metadata.init None [||] properties in
let zero = "0" in
let title = Metadata.title metadata in
let title_sort = Metadata.title_sort metadata in
let year = Metadata.year metadata in
let track = Metadata.track metadata in
let disc = Metadata.disc metadata in
let music_path = SkhcDb.MusicFile.path music in
let extension = Filename.extension music_path in
let map =
SkhcQuery.Format.Node.
[
(String Title, Option.value ~default:String.empty title);
(String SortName, Option.value ~default:String.empty title_sort);
(String Artist, names artist_names);
(String Album, SkhcDb.Album.name album);
(String AlbumArtist, names album_artists);
(String Composer, names composers);
( String Genre,
Option.value ~default:String.empty @@ SkhcDb.Album.genre album
);
(String Path, music_path);
(String FileExtension, extension);
(Bool Art, Bool.to_string @@ SkhcDb.MusicFile.has_cover music);
(Bool Flag, Bool.to_string @@ SkhcDb.MusicFile.flag music);
(Int64 Id, Printf.sprintf "%Lu" (SkhcDb.MusicFile.id music));
(Int64 Size, Printf.sprintf "%u" (SkhcDb.MusicFile.size music));
(Int Track, Option.fold ~some:string_of_int ~none:zero track);
(Int Disc, Option.fold ~some:string_of_int ~none:zero disc);
(Int Year, Option.fold ~some:string_of_int ~none:zero year);
(Int Plays, string_of_int (SkhcDb.MusicFile.play_count music));
(Int Length, string_of_int (SkhcDb.MusicFile.duration music));
( Int Rating,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.MusicFile.rating music)
);
(Int AlbumCount, string_of_int 1);
(Float AvgDuration, string_of_int (SkhcDb.MusicFile.duration music));
( Float AvgRating,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.MusicFile.rating music)
);
(Float AvgSize, Printf.sprintf "%u" (SkhcDb.MusicFile.size music));
]
in
substitute_f ~metadata map format
let music music album artist_roles properties format =
music_f music album artist_roles properties (SkhcQuery.Format.of_string format)
let album_f ~safe ~extension album stats participants format =
let artist_names, album_artists, composers = participants' participants in
let album_name = SkhcDb.Album.name album in
let artwork =
album |> SkhcDb.Album.cover_path |> Option.is_some |> Bool.to_string
in
let flag = album |> SkhcDb.Album.flag |> Bool.to_string in
let map =
SkhcQuery.Format.Node.
[
(String Title, album_name);
( String SortName,
Option.value ~default:String.empty (SkhcDb.Album.sort_name album)
);
(String Artist, names artist_names);
(String Album, album_name);
(String AlbumArtist, names album_artists);
(String Composer, names composers);
( String Genre,
Option.value ~default:String.empty @@ SkhcDb.Album.genre album
);
(Bool Art, artwork);
(Bool Flag, flag);
(Int64 Id, Printf.sprintf "%Lu" (SkhcDb.Album.id album));
(Int64 Size, Printf.sprintf "%Lu" (SkhcDb.Album.Stats.size stats));
( Int Year,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.Album.year album)
);
(Int Plays, string_of_int (SkhcDb.Album.Stats.play_count stats));
(Int Length, string_of_int (SkhcDb.Album.Stats.duration stats));
( Int Rating,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.Album.rating album)
);
(Int AlbumCount, string_of_int (SkhcDb.Album.Stats.music_count stats));
( Float AvgDuration,
string_of_float (SkhcDb.Album.Stats.avg_duration stats)
);
(Float AvgRating, string_of_float (SkhcDb.Album.Stats.avg_rating stats));
(Float AvgSize, string_of_float (SkhcDb.Album.Stats.avg_size stats));
]
in
let map =
match extension with
| None ->
map
| Some extension ->
(String FileExtension, extension) :: map
in
match safe with
| false ->
substitute_f map format
| true ->
substitute_safe_f map format
let album ~safe ~extension album stats participants format =
album_f ~safe ~extension album stats participants
(SkhcQuery.Format.of_string format)
let album_metadata_f ~safe ~extension metadata format =
let artist_names = SkhcMetadata.Metadata.artist ~sep:", " metadata in
let album_artists = SkhcMetadata.Metadata.album_artist ~sep:", " metadata in
let composers = SkhcMetadata.Metadata.composer ~sep:", " metadata in
let album_name =
Option.value ~default:String.empty @@ SkhcMetadata.Metadata.album metadata
in
let sort_name = SkhcMetadata.Metadata.album_sort metadata in
let genre = SkhcMetadata.Metadata.genre metadata in
let artwork =
metadata |> SkhcMetadata.Metadata.covers |> Array.length |> ( <> ) 0
|> Bool.to_string
in
let map =
SkhcQuery.Format.Node.
[
(String Title, album_name);
(String SortName, Option.value ~default:String.empty sort_name);
(String Artist, artist_names);
(String AlbumArtist, album_artists);
(String Album, album_name);
(String Composer, composers);
(String Genre, Option.value ~default:String.empty genre);
(Bool Art, artwork);
]
in
let map =
match extension with
| None ->
map
| Some extension ->
(String FileExtension, extension) :: map
in
match safe with
| false ->
substitute_f map format
| true ->
substitute_safe_f map format
let album_metadata ~safe ~extension metadata format =
album_metadata_f ~safe ~extension metadata (SkhcQuery.Format.of_string format)
let artist_f ~safe ~extension artist format =
let artist_name = SkhcDb.Artist.name artist in
let artwork =
artist |> SkhcDb.Artist.artwork |> Option.is_some |> Bool.to_string
in
let flag = artist |> SkhcDb.Artist.flag |> Bool.to_string in
let map =
SkhcQuery.Format.Node.
[
(Int64 Id, Printf.sprintf "%Lu" (SkhcDb.Artist.id artist));
( String SortName,
Option.value ~default:String.empty (SkhcDb.Artist.sort_name artist)
);
(String Artist, artist_name);
(String AlbumArtist, artist_name);
(String Composer, artist_name);
(Bool Art, artwork);
(Bool Flag, flag);
( Int Rating,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.Artist.rating artist)
);
( Float AvgRating,
Option.fold ~some:string_of_int ~none:String.empty
(SkhcDb.Artist.rating artist)
);
]
in
let map =
match extension with
| None ->
map
| Some extension ->
(String FileExtension, extension) :: map
in
match safe with
| false ->
substitute_f map format
| true ->
substitute_safe_f map format
let artist ~safe ~extension artist format =
artist_f ~safe ~extension artist (SkhcQuery.Format.of_string format)