winamp/Src/Plugins/Library/ml_impex/importer.cpp
2024-09-24 14:54:57 +02:00

327 lines
7.7 KiB
C++

#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;
}
}
}