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
321
322
323
324
325
326
327
|
#include "../plist/types.h"
#include "importer.h"
#include "../../General/gen_ml/ml.h"
#include <bfc/string/stringdict.h>
#include <bfc/string/url.h>
BEGIN_STRINGDICTIONARY(_itunesprops)
SDI(L"Track ID", IT_TRACKID);
SDI(L"Name", IT_NAME);
SDI(L"Artist", IT_ARTIST);
SDI(L"Album Artist", IT_ALBUMARTIST);
SDI(L"Album", IT_ALBUM);
SDI(L"Genre", IT_GENRE);
SDI(L"Comments", IT_COMMENTS);
SDI(L"Kind", IT_KIND);
SDI(L"Size", IT_SIZE);
SDI(L"Total Time", IT_TOTALTIME);
SDI(L"Track Number", IT_TRACKNUM);
SDI(L"Track Count", IT_TRACKCOUNT);
SDI(L"Year", IT_YEAR);
SDI(L"Date Modified", IT_DATEMODIFIED);
SDI(L"Date Added", IT_DATEADDED);
SDI(L"Bit Rate", IT_BITRATE);
SDI(L"Bitrate", IT_BITRATE);
SDI(L"Sample Rate", IT_SAMPLERATE);
SDI(L"Rating", IT_RATING);
SDI(L"Location", IT_LOCATION);
SDI(L"File Folder Count", IT_FOLDERCOUNT);
SDI(L"Library Folder Count", IT_LIBFOLDERCOUNT);
SDI(L"Play Count", IT_PLAYCOUNT);
SDI(L"Play Date", IT_PLAYDATE);
SDI(L"Play Date UTC", IT_PLAYDATE_UTC);
SDI(L"Composer", IT_COMPOSER);
SDI(L"Publisher", IT_PUBLISHER);
SDI(L"Disc Number", IT_DISCNUMBER);
SDI(L"Disc Count", IT_DISCCOUNT);
SDI(L"BPM", IT_BPM);
SDI(L"Has Video", IT_HAS_VIDEO);
SDI(L"Grouping", IT_GROUPING);
SDI(L"Producer", IT_PRODUCER);
SDI(L"Director", IT_DIRECTOR);
SDI(L"Artwork Count", IT_ARTWORK_COUNT);
SDI(L"Persistent ID", IT_PERSISTENT_ID);
SDI(L"Track Type", IT_TRACK_TYPE);
SDI(L"HD", IT_HD);
SDI(L"Video Width", IT_VIDEO_WIDTH);
SDI(L"Video Height", IT_VIDEO_HEIGHT);
SDI(L"Movie", IT_MOVIE);
SDI(L"Release Date", IT_RELEASE_DATE);
SDI(L"Normalization", IT_NORMALIZATION);
SDI(L"Sort Name", IT_SORTNAME);
SDI(L"Purchased", IT_PURCHASED);
SDI(L"iTunesU", IT_ITUNESU);
SDI(L"Skip Count", IT_SKIPCOUNT);
SDI(L"Skip Date", IT_SKIPDATE);
SDI(L"Sort Album", IT_SORTALBUM);
SDI(L"Sort Composer", IT_SORTCOMPOSER);
SDI(L"Part Of Gapless Album", IT_PART_OF_GAPLESS_ALBUM);
SDI(L"Compilation", IT_COMPILATION);
SDI(L"Sort Album Artist", IT_SORT_ALBUM_ARTIST);
SDI(L"Sort Artist", IT_SORT_ARTIST);
END_STRINGDICTIONARY(_itunesprops, itunesprops)
void FixPath(const wchar_t *strdata, StringW &f)
{
f = strdata;
// if the file starts with the local filename header, strip it
if (!_wcsnicmp(f, ITUNES_FILENAME_HEADER, wcslen(ITUNES_FILENAME_HEADER))) {
if (f[wcslen(ITUNES_FILENAME_HEADER)] == '/')
f = StringW(f.getValue()+wcslen(ITUNES_FILENAME_HEADER)-1);
else
f = StringW(f.getValue()+wcslen(ITUNES_FILENAME_HEADER));
// and then convert the slashes to backslashes
wchar_t *p = f.getNonConstVal();
while (p && *p) { if (*p == '/') *p = '\\'; p++; }
}
// oddly enough, iTunes XML library filenames have a trailing slash, go figure... and strip it!
if (f.lastChar() == '\\') f.trunc((int)f.len()-1);
else if (f.lastChar() == '/') f.trunc((int)f.len()-1); // if this is a url, there was no / to \ conversion
// decode %XX
Url::decode(f);
}
static void Importer_AddKeyToItemRecord(int t, const plistString *data, itemRecordW &ir)
{
const wchar_t *strdata = data->getString();
// load this property into the appropriate gen_ml field
switch (t)
{
case IT_TRACKID:
// ignored
break;
case IT_NAME:
ir.title = _wcsdup(strdata);
break;
case IT_ARTIST:
ir.artist = _wcsdup(strdata);
break;
case IT_ALBUMARTIST:
ir.albumartist = _wcsdup(strdata);
break;
case IT_ALBUM:
ir.album = _wcsdup(strdata);
break;
case IT_GENRE:
ir.genre = _wcsdup(strdata);
break;
case IT_COMMENTS:
ir.comment = _wcsdup(strdata);
break;
case IT_KIND:
// ignored
break;
case IT_LOCATION:
{
StringW f;
FixPath(strdata, f);
// done
ir.filename = _wcsdup(f);
break;
}
case IT_COMPOSER:
ir.composer = _wcsdup(strdata);
break;
case IT_PUBLISHER:
ir.publisher = _wcsdup(strdata);
break;
case IT_GROUPING:
setRecordExtendedItem(&ir, L"category", strdata);
break;
case IT_PRODUCER:
setRecordExtendedItem(&ir, L"producer", strdata);
break;
case IT_DIRECTOR:
setRecordExtendedItem(&ir, L"director", strdata);
break;
case IT_PERSISTENT_ID:
break;
case IT_TRACK_TYPE:
break;
case IT_SORTNAME:
break;
case IT_SORTALBUM:
break;
case IT_SORTCOMPOSER:
break;
case IT_SORT_ALBUM_ARTIST:
break;
case IT_SORT_ARTIST:
break;
default:
//DebugStringW(L"Unknown property: %s\n", prop->getName());
break;
}
}
static void Importer_AddKeyToItemRecord(int t, const plistInteger *data, itemRecordW &ir)
{
int64_t value = data->getValue();
/* benski> we need to keep the ones that were changed to plistBoolean,
because old exported libraries will still be written with integers */
// load this property into the appropriate gen_ml field
switch (t)
{
case IT_TRACKID:
// ignore
break;
case IT_SIZE:
ir.filesize = (int)(value >> 10);
setRecordExtendedItem(&ir, L"realsize", data->getString());
break;
case IT_TOTALTIME:
if (value)
ir.length = (int)(value / 1000);
break;
case IT_TRACKNUM:
ir.track = (int)value;
break;
case IT_TRACKCOUNT:
if (value)
ir.tracks = (int)value;
break;
case IT_YEAR:
if (value)
ir.year = (int)value;
break;
case IT_BITRATE:
ir.bitrate = (int)value;
break;
case IT_SAMPLERATE:
// ignored
break;
case IT_RATING:
ir.rating = (int)(((double)value / 100.0) * 5.0);
break;
case IT_FOLDERCOUNT:
// ignored
break;
case IT_LIBFOLDERCOUNT:
// ignored
break;
case IT_PLAYCOUNT:
if (value > 0)
ir.playcount = (int)value;
break;
case IT_PLAYDATE:
if (value)
ir.lastplay = value;
break;
case IT_DISCNUMBER:
if (value)
ir.disc = (int)value;
break;
case IT_DISCCOUNT:
if (value)
ir.discs = (int)value;
break;
case IT_BPM:
if (value)
ir.bpm = (int)value;
break;
case IT_HAS_VIDEO:
if (value == 1)
ir.type = 1;
break;
case IT_ARTWORK_COUNT:
break;
case IT_VIDEO_WIDTH:
setRecordExtendedItem(&ir, L"width", data->getString());
break;
case IT_VIDEO_HEIGHT:
setRecordExtendedItem(&ir, L"height", data->getString());
break;
case IT_NORMALIZATION:
// TODO: can we convert this to replay gain?
break;
case IT_SKIPCOUNT:
break;
default:
break;
}
}
static void Importer_AddKeyToItemRecord(int t, const plistBoolean *data, itemRecordW &ir)
{
int value = !!data->getValue();
// load this property into the appropriate gen_ml field
switch (t)
{
case IT_HAS_VIDEO:
ir.type = value;
break;
case IT_HD:
break;
case IT_MOVIE:
break;
case IT_PURCHASED:
break;
case IT_ITUNESU:
break;
case IT_PART_OF_GAPLESS_ALBUM:
break;
case IT_COMPILATION:
break;
default:
break;
}
}
static void Importer_AddKeyToItemRecord(int t, const plistDate *data, itemRecordW &ir)
{
time_t date_value= data->getDate();
// load this property into the appropriate gen_ml field
switch (t)
{
case IT_DATEMODIFIED:
if (date_value != -1)
ir.filetime = date_value;
break;
case IT_DATEADDED:
if (date_value != -1)
ir.lastupd = date_value;
break;
case IT_PLAYDATE_UTC:
if (date_value != -1)
ir.lastplay = date_value;
break;
case IT_RELEASE_DATE:
break;
case IT_SKIPDATE:
break;
default:
break;
}
}
void Importer_AddKeyToItemRecord(const plistKey *prop, itemRecordW &ir)
{
const plistData *data = prop->getData();
if (data)
{
int t = itunesprops.getId(prop->getName());
switch(data->getType())
{
case PLISTDATA_STRING:
Importer_AddKeyToItemRecord(t, (const plistString *)data, ir);
break;
case PLISTDATA_INTEGER:
Importer_AddKeyToItemRecord(t, (const plistInteger *)data, ir);
break;
case PLISTDATA_DATE:
Importer_AddKeyToItemRecord(t, (const plistDate *)data, ir);
break;
case PLISTDATA_BOOLEAN:
Importer_AddKeyToItemRecord(t, (const plistBoolean *)data, ir);
break;
default:
break;
}
}
}
|