diff options
Diffstat (limited to 'Src/Plugins/Library/ml_impex')
-rw-r--r-- | Src/Plugins/Library/ml_impex/ImportPlaylists.cpp | 404 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ImporterAPI.cpp | 299 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ImporterAPI.h | 13 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/api__ml_impex.h | 20 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/api_importer.h | 52 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/impex.cpp | 625 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/importer.cpp | 327 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/importer.h | 66 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp | 152 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/itunesxmlwrite.h | 33 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ml_impex.rc | 144 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ml_impex.sln | 50 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ml_impex.vcxproj | 317 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/ml_impex.vcxproj.filters | 62 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/resource.h | 37 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_impex/version.rc2 | 39 |
16 files changed, 2640 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_impex/ImportPlaylists.cpp b/Src/Plugins/Library/ml_impex/ImportPlaylists.cpp new file mode 100644 index 00000000..ccb75281 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ImportPlaylists.cpp @@ -0,0 +1,404 @@ +#include "api__ml_impex.h" +#include "../xml/obj_xml.h" +#include <map> +#include <bfc/string/url.h> +#include "importer.h" +#include "resource.h" +#include "../plist/loader.h" +#include "../playlist/ifc_playlist.h" +#include "../Winamp/wa_ipc.h" +#include <shlwapi.h> + +struct iTunesFileInfo +{ + iTunesFileInfo(const wchar_t *_filename, uint64_t _length) + { + filename = _wcsdup(_filename); + length = _length; + } + ~iTunesFileInfo() + { + free(filename); + } + wchar_t *filename; + uint64_t length; +}; +typedef std::map<int64_t, iTunesFileInfo*> FilesList; +extern winampMediaLibraryPlugin plugin; +int Load(const wchar_t *filename, obj_xml *parser); + +class PlistPlaylist : public ifc_playlist +{ +public: + PlistPlaylist(const plistArray *_items, FilesList &_files) : items(_items), files(_files) + { + length_sum = 0; + } + size_t GetNumItems(); + size_t GetItem(size_t item, wchar_t *filename, size_t filenameCch); + int GetItemLengthMilliseconds(size_t item); + uint64_t length_sum; +protected: + RECVS_DISPATCH; + const plistArray *items; + FilesList &files; +}; + +size_t PlistPlaylist::GetNumItems() +{ + return items->getNumItems(); +} + +size_t PlistPlaylist::GetItem(size_t item, wchar_t *filename, size_t filenameCch) +{ + plistDict *item_dict = (plistDict *)items->enumItem((int)item); + if (item_dict) + { + plistKey *id_key = item_dict->getKey(L"Track ID"); + if (id_key) + { + plistInteger *id_data = (plistInteger *)id_key->getData(); + if (id_data) + { + int64_t key = id_data->getValue(); + iTunesFileInfo *info = files[key]; + if (info) + { + const wchar_t *track_name = info->filename; + if (track_name) + { + length_sum += info->length; + StringCchCopyW(filename, filenameCch, track_name); + return 1; + } + } + } + } + } + return 0; +} + +int PlistPlaylist::GetItemLengthMilliseconds(size_t item) +{ + plistDict *item_dict = (plistDict *)items->enumItem((int)item); + if (item_dict) + { + plistKey *id_key = item_dict->getKey(L"Track ID"); + if (id_key) + { + plistInteger *id_data = (plistInteger *)id_key->getData(); + if (id_data) + { + int64_t key = id_data->getValue(); + iTunesFileInfo *info = files[key]; + if (info) + { + return (int)info->length; + } + } + } + } + return 0; +} + +#define CBCLASS PlistPlaylist +START_DISPATCH; +CB(IFC_PLAYLIST_GETNUMITEMS, GetNumItems) +CB(IFC_PLAYLIST_GETITEM, GetItem) +CB(IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds) +END_DISPATCH; +#undef CBCLASS + +static bool GetInteger(const plistDict *dict, const wchar_t *key_name, int64_t *int_val) +{ + plistKey *key = dict->getKey(key_name); + if (!key) + return false; + + plistData *data = key->getData(); + if (!data) + return false; + + if (data->getType() != PLISTDATA_INTEGER) + return false; + + plistInteger *data_int = static_cast<plistInteger *>(data); + *int_val = data_int->getValue(); + return true; +} + +static bool GetString(const plistDict *dict, const wchar_t *key_name, const wchar_t **str_val) +{ + plistKey *key = dict->getKey(key_name); + if (!key) + return false; + + plistData *data = key->getData(); + if (!data) + return false; + + if (data->getType() != PLISTDATA_STRING) + return false; + + plistString *data_str = static_cast<plistString *>(data); + *str_val = data_str->getString(); + return true; +} + +static bool GetArray(const plistDict *dict, const wchar_t *key_name, plistArray **array_val) +{ + plistKey *key = dict->getKey(key_name); + if (!key) + return false; + + plistData *data = key->getData(); + if (!data) + return false; + + if (data->getType() != PLISTDATA_ARRAY) + return false; + + *array_val = static_cast<plistArray *>(data); + return true; +} + +static bool CheckDuplicatePlaylist(uint64_t playlist_id64, GUID &dup_guid) +{ + AGAVE_API_PLAYLISTS->Lock(); + size_t numPlaylists = AGAVE_API_PLAYLISTS->GetCount(); + uint64_t compare_id64=0; + for (size_t i=0;i!=numPlaylists;i++) + { + if (AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_iTunesID, &compare_id64, sizeof(compare_id64)) == API_PLAYLISTS_SUCCESS) + { + if (compare_id64 == playlist_id64) + { + dup_guid = AGAVE_API_PLAYLISTS->GetGUID(i); + AGAVE_API_PLAYLISTS->Unlock(); + return true; + } + } + } + AGAVE_API_PLAYLISTS->Unlock(); + return false; +} + +enum +{ + DUPLICATE_PLAYLIST_SKIP, + DUPLICATE_PLAYLIST_REPLACE, + DUPLICATE_PLAYLIST_NEW, +}; + +static int PromptReplaceSkipNew(GUID &dup_guid) +{ + /* TODO: + * get name and stuff from api_playlists + * we'll need an HWND for the UI + * we'll need some passed-in state variable to remember "do for all" choice + */ + return DUPLICATE_PLAYLIST_SKIP; +} + +HINSTANCE cloud_hinst = 0; +int IPC_GET_CLOUD_HINST = -1, IPC_GET_CLOUD_ACTIVE = -1; +int cloudAvailable() +{ + if (IPC_GET_CLOUD_HINST == -1) IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_GET_CLOUD_ACTIVE == -1) IPC_GET_CLOUD_ACTIVE = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloudActive", IPC_REGISTER_WINAMP_IPCMESSAGE); + if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST); + + return (/*0/*/!(!cloud_hinst || cloud_hinst == (HINSTANCE)1 || !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))/**/); +} + +static void AddPlaylist(plistDict *playlist, FilesList &files) +{ + const wchar_t *name; + const wchar_t *playlist_persistent_id; + int64_t playlist_id; + int64_t visible; + plistArray *items; + uint64_t playlist_id64; + + if (GetString(playlist, L"Name", &name) + && GetArray(playlist, L"Playlist Items", &items) + && GetInteger(playlist, L"Playlist ID", &playlist_id) + && GetString(playlist, L"Playlist Persistent ID", &playlist_persistent_id) + && (!GetInteger(playlist, L"Visible", &visible) || visible)) + { + playlist_id64 =_wcstoui64(playlist_persistent_id, 0, 16); + // see if it's already in the database + + GUID dup_guid; // so we know the GUID we clash with, in case we want to replace it instead of skip it + if (playlist_id64 && CheckDuplicatePlaylist(playlist_id64, dup_guid)) + { + switch(PromptReplaceSkipNew(dup_guid)) + { + case DUPLICATE_PLAYLIST_SKIP: + break; + case DUPLICATE_PLAYLIST_REPLACE: + // TODO + break; + case DUPLICATE_PLAYLIST_NEW: + // TODO + break; + } + } + else + { + PlistPlaylist plist_playlist(items, files); + + const wchar_t *user_folder = WASABI_API_APP->path_getUserSettingsPath(); + wchar_t destination[MAX_PATH] = {0}; + PathCombineW(destination, user_folder, L"plugins\\ml\\playlists"); + + wchar_t playlist_filename[MAX_PATH] = {0}; + StringCbPrintfW(playlist_filename, sizeof(playlist_filename), L"i_%I64u.m3u8", playlist_id); + PathAppendW(destination, playlist_filename); + + static wchar_t ml_ini_file[MAX_PATH] = {0}; + if (!ml_ini_file[0]) lstrcpynW(ml_ini_file, (const wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW), MAX_PATH); + size_t cloud = (cloudAvailable() ? GetPrivateProfileIntW(L"gen_ml_config", L"cloud_always", 1, ml_ini_file) : 0); + + AGAVE_API_PLAYLISTMANAGER->Save(destination, &plist_playlist); + AGAVE_API_PLAYLISTS->Lock(); + int new_index = (!cloud ? (int)AGAVE_API_PLAYLISTS->AddPlaylist(destination, name) : (int)AGAVE_API_PLAYLISTS->AddCloudPlaylist(destination, name)); + if (new_index >= 0) + { + uint32_t numItems = (uint32_t)plist_playlist.GetNumItems(); + uint64_t totalLength = plist_playlist.length_sum/1000; + AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_totalTime, &totalLength, sizeof(totalLength)); + AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_itemCount, &numItems, sizeof(numItems)); + if (cloud) AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_cloud, &cloud, sizeof(cloud)); + AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_iTunesID, &playlist_id64, sizeof(playlist_id64)); + } + AGAVE_API_PLAYLISTS->Unlock(); + + PostMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"ml_playlist_refresh", IPC_REGISTER_WINAMP_IPCMESSAGE)); + } + } +} + +void FixPath(const wchar_t *strdata, StringW &f); + +int ImportPlaylists(HWND parent, const wchar_t *library_file) +{ + FilesList files; + // create an XML parser + obj_xml *parser=0; + waServiceFactory *factory = plugin.service->service_getServiceByGuid(obj_xmlGUID); + if (factory) + parser = (obj_xml *)factory->getInterface(); + + if (parser) + { + // create status window + //HWND hwndDlg = WASABI_API_CREATEDIALOGW(IDD_INFODIALOG,plugin.hwndLibraryParent,import_dlgproc); + + // init it + //ShowWindow(hwndDlg, SW_NORMAL); + //UpdateWindow(hwndDlg); + + // create an iTunes XML library reader + plistLoader it; + + // load the XML, this creates an iTunes DB in memory, and returns the root key + parser->xmlreader_open(); + parser->xmlreader_registerCallback(L"plist\f*", &it); + Load(library_file, parser); + parser->xmlreader_unregisterCallback(&it); + parser->xmlreader_close(); + plistKey *root_key = ⁢ + + // show import progress controls + //ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSING_STATE), SW_HIDE); + //ShowWindow(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), SW_SHOWNORMAL); + //ShowWindow(GetDlgItem(hwndDlg, IDC_TRACKS), SW_SHOWNORMAL); + //UpdateWindow(hwndDlg); + + // we start at the root key + if (root_key) + { + // the root key contains a dictionary + plistData *root_dict = root_key->getData(); + if (root_dict && root_dict->getType() == PLISTDATA_DICT) + { + // that dictionary contains a number of keys, one of which contains a dictionary of tracks + plistKey *tracks_key = ((plistDict*)root_dict)->getKey(L"Tracks"); + plistData *tracks_dict = tracks_key?tracks_key->getData():0; + if (tracks_dict && tracks_dict->getType() == PLISTDATA_DICT) + { + // we have the tracks dictionary ... + plistDict *tracks = (plistDict *)tracks_dict; + int n =tracks?tracks->getNumKeys():0; + // ... now enumerate tracks + for (int i=0;i<n;i++) + { + // each track is a key in the tracks dictionary, and contains a dictionary of properties + plistKey *track_key = tracks->enumKey(i); + plistData *track_dict = track_key->getData(); + // prepare an item record + + if (track_dict->getType() == PLISTDATA_DICT) + { + // we have the track's dictionary of properties... + plistDict *track = (plistDict *)track_dict; + int64_t id = 0; + const wchar_t *location = 0; + if (GetInteger(track, L"Track ID", &id) && GetString(track, L"Location", &location)) + { + StringW f; + FixPath(location, f); + + int64_t length = 0; + GetInteger(track, L"Total Time", &length); + // done + wchar_t *filename = _wcsdup(f); + files[id] = new iTunesFileInfo(filename, length); + + // show progress + //SetDlgItemText(hwndDlg, IDC_TRACKS, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TRACKS_IMPORTED_X), ++count)); + //SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, (int)((double)count/n*100.0), 0); + //if (count % 10 == 0 || count == n) + // UpdateWindow(hwndDlg); + } + } + } + } + + // ok we're done building the track list, now let's enumerate the playlists + plistKey *playlists_key = ((plistDict*)root_dict)->getKey(L"Playlists"); + plistData *playlists_dict = playlists_key?playlists_key->getData():0; + if (playlists_dict && playlists_dict->getType() == PLISTDATA_ARRAY) + { + plistArray *playlists = (plistArray *)playlists_dict; + int n =playlists?playlists->getNumItems():0; + // ... now enumerate playlists + for (int i=0;i<n;i++) + { + // each playlist is a key in the playlists dictionary, and contains a dictionary of properties + plistData *playlist_dict = playlists->enumItem(i); + if (playlist_dict->getType() == PLISTDATA_DICT) + { + // we have the playlist's dictionary of properties... + plistDict *playlist = (plistDict *)playlist_dict; + AddPlaylist(playlist, files); + } + } + } + } + } + //DestroyWindow(hwndDlg); + factory->releaseInterface(parser); + } + else + return DISPATCH_FAILURE; + + FilesList::iterator itr; + for (itr = files.begin(); itr!= files.end(); itr++) + { + iTunesFileInfo *info = itr->second; + delete info; + } + return DISPATCH_SUCCESS; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/ImporterAPI.cpp b/Src/Plugins/Library/ml_impex/ImporterAPI.cpp new file mode 100644 index 00000000..3961b8ea --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ImporterAPI.cpp @@ -0,0 +1,299 @@ +#include "ImporterAPI.h" +#include "../xml/obj_xml.h" +#include "importer.h" +#include "resource.h" +#include "api__ml_impex.h" +#include "../plist/loader.h" +#include <api/service/waservicefactory.h> +#include <shlobj.h> + +extern winampMediaLibraryPlugin plugin; + +int Load(const wchar_t *filename, obj_xml *parser) +{ + if (!parser) + return 1; // no sense in continuing if there's no parser available + + HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); + + if (file == INVALID_HANDLE_VALUE) + return 1; + + while (true) + { + char data[1024] = {0}; + DWORD bytesRead = 0; + if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead) + { + parser->xmlreader_feed(data, bytesRead); + } + else + break; + } + + CloseHandle(file); + parser->xmlreader_feed(0, 0); + return 0; +} + +static bool GetiTunesPreferencesPath(wchar_t *path, size_t path_cch) +{ + wchar_t appdata[MAX_PATH] = {0}; + SHGetSpecialFolderPathW(NULL, appdata, CSIDL_APPDATA, FALSE); + StringCchPrintf(path, path_cch, L"%s\\Apple Computer\\iTunes\\iTunesPrefs.xml", appdata); + return true; +} + +static bool GetiTunesLibraryPath(wchar_t *path, size_t path_cch) +{ + wchar_t itunes_pref[MAX_PATH] = {0}; + if (GetiTunesPreferencesPath(itunes_pref, MAX_PATH)) + { + plistLoader it; + + obj_xml *parser=0; + waServiceFactory *factory = plugin.service->service_getServiceByGuid(obj_xmlGUID); + if (factory) + parser = (obj_xml *)factory->getInterface(); + + if (parser) + { + // load the XML, this creates an iTunes DB in memory, and returns the root key + parser->xmlreader_open(); + parser->xmlreader_registerCallback(L"plist\f*", &it); + Load(itunes_pref, parser); + parser->xmlreader_unregisterCallback(&it); + parser->xmlreader_close(); + plistKey *root_key = ⁢ + plistData *root_dict = root_key->getData(); + if (root_dict) + { + plistKey *prefs_key = ((plistDict*)root_dict)->getKey(L"User Preferences"); + if (prefs_key) + { + plistData *prefs_dict= prefs_key->getData(); + if (prefs_dict) + { + plistKey *location_key = ((plistDict*)prefs_dict)->getKey(L"iTunes Library XML Location:1"); + if (location_key) + { + plistData *location_data = location_key->getData(); + if (location_data) + { + plistRaw *location_data_raw = (plistRaw *)location_data; + if (location_data_raw) + { + int size; + const wchar_t *mem = (const wchar_t *)location_data_raw->getMem(&size); + if (mem) + { + memcpy(path, mem, size); + path[size/2]=0; + return true; + } + } + } + } + } + } + } + } + } + return false; + +} + +// ----------------------------------------------------------------------- +// import status window proc +// ----------------------------------------------------------------------- +static BOOL CALLBACK import_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + + // show import progress controls + ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSING_STATE), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), SW_SHOWNORMAL); + ShowWindow(GetDlgItem(hwndDlg, IDC_TRACKS), SW_SHOWNORMAL); + + SetWindowText(hwndDlg,WASABI_API_LNGSTRINGW(IDS_IMPORTING_DATABASE)); + SetDlgItemText(hwndDlg, IDC_PROCESSING_STATE, WASABI_API_LNGSTRINGW(IDS_LOADING_XML)); + SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, 0, 0); + + setDialogIcon(hwndDlg); + + SetTimer(hwndDlg, 666, 250, 0); + SetForegroundWindow(hwndDlg); + return 0; + } + + case WM_TIMER: + if (wParam == 666) + { + int *progress = (int *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (progress[0] == -666) + { + KillTimer(hwndDlg, 666); + EndDialog(hwndDlg, 0); + } + else + { + // display progress + SetDlgItemText(hwndDlg, IDC_TRACKS, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TRACKS_IMPORTED_X), progress[0])); + SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, (int)((double)progress[0]/progress[1]*100.0), 0); + } + } + break; + + case WM_COMMAND: + { + int wID = LOWORD(wParam); + switch (wID) { + case IDOK: + case IDCANCEL: + EndDialog(hwndDlg, wID); + break; + } + } + return 0; + } + return 0; +} + +static DWORD CALLBACK ImportThread(LPVOID param) +{ + WASABI_API_DIALOGBOXPARAMW(IDD_INFODIALOG, NULL, import_dlgproc, (LPARAM)param); + return 0; +} + +int ImporterAPI::ImportFromFile(HWND parent, const wchar_t *library_file) +{ + // create an XML parser + obj_xml *parser=0; + waServiceFactory *factory = plugin.service->service_getServiceByGuid(obj_xmlGUID); + if (factory) + parser = (obj_xml *)factory->getInterface(); + + if (parser) + { + // create status window + int progress[2] = {0}; + DWORD threadId = 0; + HANDLE importThread = CreateThread(0, 0, ImportThread, progress, 0, &threadId); + + // create an iTunes XML library reader + plistLoader it; + + // load the XML, this creates an iTunes DB in memory, and returns the root key + parser->xmlreader_open(); + parser->xmlreader_registerCallback(L"plist\f*", &it); + Load(library_file, parser); + parser->xmlreader_unregisterCallback(&it); + parser->xmlreader_close(); + plistKey *root_key = ⁢ + + // we start at the root key + if (root_key) { + // the root key contains a dictionary + plistData *root_dict = root_key->getData(); + if (root_dict && root_dict->getType() == PLISTDATA_DICT) { + // that dictionary contains a number of keys, one of which contains a dictionary of tracks + plistKey *tracks_key = ((plistDict*)root_dict)->getKey(L"Tracks"); + plistData *tracks_dict = tracks_key?tracks_key->getData():0; + if (tracks_dict && tracks_dict->getType() == PLISTDATA_DICT) { + // we have the tracks dictionary ... + plistDict *tracks = (plistDict *)tracks_dict; + progress[1]=tracks?tracks->getNumKeys():0; + // ... now enumerate tracks + for (int i=0;i<progress[1];i++) { + // each track is a key in the tracks dictionary, and contains a dictionary of properties + plistKey *track_key = tracks->enumKey(i); + plistData *track_dict = track_key->getData(); + // prepare an item record + itemRecordW ir; + MEMZERO(&ir, sizeof(ir)); + ir.year = ir.track = ir.length = -1; + ir.lastplay = -1; + ir.type = 0; // this makes it an Audio file (unless otherwise specified + if (track_dict->getType() == PLISTDATA_DICT) { + // we have the track's dictionary of properties... + plistDict *track = (plistDict *)track_dict; + int tn = track->getNumKeys(); + // ... now enumerate the properties + for (int j=0;j<tn;j++) { + plistKey *prop = track->enumKey(j); + Importer_AddKeyToItemRecord(prop, ir); + } + // add or update the file + SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&ir, ML_IPC_DB_ADDORUPDATEITEMW); + // free the record + freeRecord(&ir); + // show progress + ++progress[0]; + } + } + } + } + } + + //done + progress[0]=-666; + if (importThread) + { + WaitForSingleObject(importThread, INFINITE); + CloseHandle(importThread); + } + + // tell gen_ml we modified the db + SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB); + + factory->releaseInterface(parser); + } + else + return DISPATCH_FAILURE; + return DISPATCH_SUCCESS; +} + +bool ImporterAPI::iTunesExists() +{ + wchar_t itunes_path[MAX_PATH] = {0}; + if (GetiTunesPreferencesPath(itunes_path, MAX_PATH)) + { + return GetFileAttributesW(itunes_path) != INVALID_FILE_ATTRIBUTES; + } + return false; +} + +int ImporterAPI::ImportFromiTunes(HWND parent) +{ + wchar_t itunes_path[MAX_PATH] = {0}; + if (GetiTunesLibraryPath(itunes_path, MAX_PATH)) + { + return ImportFromFile(parent, itunes_path); + } + return DISPATCH_FAILURE; +} + +int ImportPlaylists(HWND parent, const wchar_t *library_file); +int ImporterAPI::ImportPlaylistsFromiTunes(HWND parent) +{ + wchar_t itunes_path[MAX_PATH] = {0}; + if (GetiTunesLibraryPath(itunes_path, MAX_PATH)) + { + return ImportPlaylists(parent, itunes_path); + } + return DISPATCH_FAILURE; +} + +#define CBCLASS ImporterAPI +START_DISPATCH; +CB(API_ITUNES_IMPORTER_ITUNESEXISTS, iTunesExists) +CB(API_ITUNES_IMPORTER_IMPORTFROMFILE, ImportFromFile) +CB(API_ITUNES_IMPORTER_IMPORTFROMITUNES, ImportFromiTunes) +CB(API_ITUNES_IMPORTER_IMPORTPLAYLISTSFROMITUNES, ImportPlaylistsFromiTunes) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/ImporterAPI.h b/Src/Plugins/Library/ml_impex/ImporterAPI.h new file mode 100644 index 00000000..728f2359 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ImporterAPI.h @@ -0,0 +1,13 @@ +#include "api_importer.h" + +class ImporterAPI : public api_itunes_importer +{ +public: + bool iTunesExists(); + int ImportFromFile(HWND parent, const wchar_t *library_file); + int ImportFromiTunes(HWND parent); + int ImportPlaylistsFromiTunes(HWND parent); + +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/api__ml_impex.h b/Src/Plugins/Library/ml_impex/api__ml_impex.h new file mode 100644 index 00000000..8b137122 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/api__ml_impex.h @@ -0,0 +1,20 @@ +#ifndef NULLSOFT_ML_IMPEX_API_H +#define NULLSOFT_ML_IMPEX_API_H + +#include <api/service/waservicefactory.h> + +#include "../Agave/Language/api_language.h" + +#include <api/application/api_application.h> +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include "../playlist/api_playlistmanager.h" +extern api_playlistmanager *playlistManager; +#define AGAVE_API_PLAYLISTMANAGER playlistManager + +#include "../playlist/api_playlists.h" +extern api_playlists *playlistsApi; +#define AGAVE_API_PLAYLISTS playlistsApi + +#endif // !NULLSOFT_ML_IMPEX_API_H diff --git a/Src/Plugins/Library/ml_impex/api_importer.h b/Src/Plugins/Library/ml_impex/api_importer.h new file mode 100644 index 00000000..1d9dc433 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/api_importer.h @@ -0,0 +1,52 @@ +#pragma once + +#include <bfc/dispatch.h> +#include <api/service/services.h> + +// {A32C39BC-CDF7-4c9b-9EA2-9DF7E0D651D2} +static const GUID iTunesImporterGUID = +{ 0xa32c39bc, 0xcdf7, 0x4c9b, { 0x9e, 0xa2, 0x9d, 0xf7, 0xe0, 0xd6, 0x51, 0xd2 } }; + +class api_itunes_importer : public Dispatchable +{ +protected: + api_itunes_importer() {} + ~api_itunes_importer() {} +public: + static FOURCC getServiceType() { return WaSvc::UNIQUE; } + static const char *getServiceName() { return "iTunes Importer Service"; } + static GUID getServiceGuid() { return iTunesImporterGUID; } + + bool iTunesExists(); + int ImportFromFile(HWND parent, const wchar_t *library_file); + int ImportFromiTunes(HWND parent); + int ImportPlaylistsFromiTunes(HWND parent); + + enum + { + API_ITUNES_IMPORTER_ITUNESEXISTS = 0, + API_ITUNES_IMPORTER_IMPORTFROMFILE = 1, + API_ITUNES_IMPORTER_IMPORTFROMITUNES = 2, + API_ITUNES_IMPORTER_IMPORTPLAYLISTSFROMITUNES = 3, + }; +}; + +inline bool api_itunes_importer::iTunesExists() +{ + return _call(API_ITUNES_IMPORTER_ITUNESEXISTS, (bool)false); +} + +inline int api_itunes_importer::ImportFromFile(HWND parent, const wchar_t *library_file) +{ + return _call(API_ITUNES_IMPORTER_IMPORTFROMFILE, (int)DISPATCH_FAILURE, parent, library_file); +} + +inline int api_itunes_importer::ImportFromiTunes(HWND parent) +{ + return _call(API_ITUNES_IMPORTER_IMPORTFROMITUNES, (int)DISPATCH_FAILURE, parent); +} + +inline int api_itunes_importer::ImportPlaylistsFromiTunes(HWND parent) +{ + return _call(API_ITUNES_IMPORTER_IMPORTPLAYLISTSFROMITUNES, (int)DISPATCH_FAILURE, parent); +} diff --git a/Src/Plugins/Library/ml_impex/impex.cpp b/Src/Plugins/Library/ml_impex/impex.cpp new file mode 100644 index 00000000..f37eaa65 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/impex.cpp @@ -0,0 +1,625 @@ +//------------------------------------------------------------------------ +// +// iTunes XML Import/Export Plugin +// Copyright © 2003-2014 Winamp SA +// +//------------------------------------------------------------------------ +//#define PLUGIN_NAME "Nullsoft Database Import/Export" +#define PLUGIN_VERSION L"2.65" + +#include <windows.h> +#include <commdlg.h> +#include <commctrl.h> +#include <stdio.h> +#include "api__ml_impex.h" +#include "../../General/gen_ml/ml.h" +#include "../winamp/wa_ipc.h" +#include "resource.h" +#include <bfc/string/url.h> +#include "itunesxmlwrite.h" +#include "importer.h" +#include "ImporterAPI.h" +#include "../nu/Singleton.h" +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" + +static ImporterAPI importAPI; +static SingletonServiceFactory<api_itunes_importer, ImporterAPI> importerFactory; +// ----------------------------------------------------------------------- + +#define ID_IMPORT 35443 +#define ID_EXPORT 35444 +#define ID_CVTPLAYLISTS 35445 +#define ID_IMPORT_ITUNES 35446 + +#define ID_FILE_ADDTOLIBRARY 40344 +#define ID_DOSHITMENU_ADDNEWVIEW 40030 +#define IDM_LIBRARY_CONFIG 40050 +#define ID_DOSHITMENU_ADDNEWPLAYLIST 40031 +#define WINAMP_MANAGEPLAYLISTS 40385 + +// ----------------------------------------------------------------------- + +api_application *WASABI_API_APP = 0; +api_playlistmanager *AGAVE_API_PLAYLISTMANAGER = 0; +api_playlists *AGAVE_API_PLAYLISTS = 0; + +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +wchar_t* GetFilterListString(void) { // "iTunes XML Library\0*.xml\0\0" + static wchar_t filterString[128] = {0}; + wchar_t* end = 0; + StringCchCopyEx(filterString, 128, WASABI_API_LNGSTRINGW(IDS_ITUNES_XML_LIBRARY), &end, 0, 0); + StringCchCopyEx(end+1, 128, L"*.xml", 0, 0, 0); + return filterString; +} + +// ----------------------------------------------------------------------- + +static LRESULT WINAPI ml_newParentWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +static LRESULT WINAPI ml_newMlWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +static WNDPROC ml_oldParentWndProc; +static WNDPROC ml_oldMlWndProc; +static HWND export_wnd, hwnd_winamp, mlWnd; +extern winampMediaLibraryPlugin plugin; +HMENU mlMenu=NULL; +void exportDatabase(); +void importDatabase(); + +// ----------------------------------------------------------------------- +// dummy plugin message procs, we just ignore everything +// ----------------------------------------------------------------------- +static INT_PTR PluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + if (message_type == ML_MSG_NO_CONFIG) + return TRUE; + + return FALSE; +} + +// ----------------------------------------------------------------------- +// plugin, exported to gen_ml +// ----------------------------------------------------------------------- +static int init(); +static void quit(); + +extern "C" winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "nullsoft(ml_impex.dll)", + init, + quit, + PluginMessageProc, + 0, + 0, + 0, +}; + +// ----------------------------------------------------------------------- +// export +// ----------------------------------------------------------------------- +extern "C" { + __declspec( dllexport ) winampMediaLibraryPlugin * winampGetMediaLibraryPlugin() + { + return &plugin; + } +}; + +// ----------------------------------------------------------------------- +// returns the position of a command within an HMENU +// ----------------------------------------------------------------------- +int getMenuItemPos(HMENU menu, UINT command) { + for (int i=0;i<256;i++) { + MENUITEMINFO mii={sizeof(mii),MIIM_ID,}; + if (!GetMenuItemInfo(menu, i, TRUE, &mii)) break; + if (mii.wID == command) return i; + } + return -1; +} + +// ----------------------------------------------------------------------- +// entry point, gen_ml is starting up and we've just been loaded +// ----------------------------------------------------------------------- +int init() +{ + waServiceFactory *sf = plugin.service->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface()); + + sf = plugin.service->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface()); + + sf = plugin.service->service_getServiceByGuid(api_playlistmanagerGUID); + if (sf) AGAVE_API_PLAYLISTMANAGER = reinterpret_cast<api_playlistmanager*>(sf->getInterface()); + + sf = plugin.service->service_getServiceByGuid(api_playlistsGUID); + if (sf) AGAVE_API_PLAYLISTS = reinterpret_cast<api_playlists*>(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(plugin.hDllInstance,MlImpexLangGUID); + + importerFactory.Register(plugin.service, &importAPI); + + static wchar_t szDescription[256]; + StringCchPrintf(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_ML_IMPEX_DESC), PLUGIN_VERSION); + plugin.description = (char*)szDescription; + + HWND w = plugin.hwndWinampParent; + while (GetParent(w) != NULL) w = GetParent(w); + hwnd_winamp = w; + + ml_oldParentWndProc = (WNDPROC)SetWindowLongPtrW(plugin.hwndWinampParent, GWLP_WNDPROC, (LONG_PTR)ml_newParentWndProc); + + mlMenu = (HMENU)SendMessage(hwnd_winamp, WM_WA_IPC, 9, IPC_GET_HMENU); + int IPC_GETMLWINDOW=(int)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); + mlWnd = (HWND)SendMessage(hwnd_winamp, WM_WA_IPC, -1, IPC_GETMLWINDOW); + + ml_oldMlWndProc = (WNDPROC)SetWindowLongPtrW(mlWnd, GWLP_WNDPROC, (LONG_PTR)ml_newMlWndProc); + + int p = getMenuItemPos(mlMenu, ID_FILE_ADDTOLIBRARY); + MENUITEMINFO mii={sizeof(mii),MIIM_ID|MIIM_TYPE, MFT_SEPARATOR, }; + InsertMenuItem(mlMenu, ++p, TRUE, &mii); + + if (importAPI.iTunesExists()) + { + MENUITEMINFO mii2={sizeof(mii2),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_IMPORT_ITUNES, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_ITUNES_DB), 0}; + InsertMenuItem(mlMenu, ++p, TRUE, &mii2); + MENUITEMINFO mii5={sizeof(mii5),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_CVTPLAYLISTS, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_ITUNES_PL), 0}; + InsertMenuItem(mlMenu, ++p, TRUE, &mii5); + } + MENUITEMINFO mii3={sizeof(mii),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_IMPORT, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_DATABASE), 0}; + InsertMenuItem(mlMenu, ++p, TRUE, &mii3); + MENUITEMINFO mii4={sizeof(mii),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_EXPORT, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_EXPORT_DATABASE), 0}; + InsertMenuItem(mlMenu, ++p, TRUE, &mii4); + + int IPC_GET_ML_HMENU = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibraryGetHmenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + HMENU context_menu = (HMENU) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_ML_HMENU); + if (context_menu) + { + HMENU hmenuPopup = GetSubMenu(context_menu, 0); + if (hmenuPopup) + { + int p = getMenuItemPos(hmenuPopup, WINAMP_MANAGEPLAYLISTS); + if (getMenuItemPos(hmenuPopup, IDM_LIBRARY_CONFIG) != -1) // sanity check + { + bool end_separator=true; + if (p != -1) + { + MENUITEMINFO mii={sizeof(mii),MIIM_ID|MIIM_TYPE, MFT_SEPARATOR, }; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii); + end_separator=false; + } + if (importAPI.iTunesExists()) + { + MENUITEMINFO mii2={sizeof(mii2),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_IMPORT_ITUNES, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_ITUNES_DB), 0}; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii2); + MENUITEMINFO mii5={sizeof(mii5),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_CVTPLAYLISTS, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_ITUNES_PL), 0}; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii5); + } + MENUITEMINFO mii3={sizeof(mii3),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_IMPORT, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_IMPORT_DATABASE), 0}; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii3); + MENUITEMINFO mii4={sizeof(mii4),MIIM_ID|MIIM_STATE|MIIM_TYPE, MFT_STRING, MFS_ENABLED, ID_EXPORT, NULL, NULL, NULL, NULL, WASABI_API_LNGSTRINGW(IDS_EXPORT_DATABASE), 0}; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii4); + if (end_separator) + { + MENUITEMINFO mii={sizeof(mii),MIIM_ID|MIIM_TYPE, MFT_SEPARATOR, }; + InsertMenuItem(hmenuPopup, ++p, TRUE, &mii); + } + } + } + } + + return ML_INIT_SUCCESS; +} + +// ----------------------------------------------------------------------- +// entry point, gen_ml is shutting down and we are being unloaded +// ----------------------------------------------------------------------- +void quit() { + if (IsWindow(plugin.hwndWinampParent)) SetWindowLongPtrW(plugin.hwndWinampParent, GWLP_WNDPROC, (LONG_PTR)ml_oldParentWndProc); + if (IsWindow(mlWnd)) SetWindowLongPtrW(mlWnd, GWLP_WNDPROC, (LONG_PTR)ml_oldMlWndProc); + importerFactory.Deregister(plugin.service); +} + +void handleMenuItem(int wID) { + switch (wID) { + case ID_EXPORT: + exportDatabase(); + break; + case ID_IMPORT: + importDatabase(); + break; + case ID_IMPORT_ITUNES: + { + importAPI.ImportFromiTunes(plugin.hwndLibraryParent); + } + break; + case ID_CVTPLAYLISTS: + { + importAPI.ImportPlaylistsFromiTunes(plugin.hwndLibraryParent); + } + break; + } +} + +void setDialogIcon(HWND hwndDlg) +{ + static HICON wa_icy; + if (wa_icy) + { + wa_icy = (HICON)LoadImage(GetModuleHandle(L"winamp.exe"), + MAKEINTRESOURCE(102), IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION); + } + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)wa_icy); +} + +// ----------------------------------------------------------------------- +// library HWND subclass +// ----------------------------------------------------------------------- +static LRESULT WINAPI ml_newParentWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_COMMAND: + { + int wID = LOWORD(wParam); + handleMenuItem(wID); + } + break; + } + return CallWindowProcW(ml_oldParentWndProc,hwndDlg,uMsg,wParam,lParam); +} + +// ----------------------------------------------------------------------- +static LRESULT WINAPI ml_newMlWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_COMMAND: + { + int wID = LOWORD(wParam); + handleMenuItem(wID); + } + break; + } + return CallWindowProcW(ml_oldMlWndProc,hwndDlg,uMsg,wParam,lParam); +} + +// {55334B63-68D5-4389-A209-797F123E207F} +static const GUID ML_IMPEX = +{ 0x55334b63, 0x68d5, 0x4389, { 0xa2, 0x9, 0x79, 0x7f, 0x12, 0x3e, 0x20, 0x7f } }; + +//------------------------------------------------------------------------ +// pick an input file +//------------------------------------------------------------------------ +static int pickFile(HWND hwndDlg, StringW *file) +{ + wchar_t oldCurPath[MAX_PATH] = {0}; + GetCurrentDirectoryW(MAX_PATH, oldCurPath); + + OPENFILENAME l={sizeof(l),}; + wchar_t *temp; + const int len=256*1024-128; + + temp = (wchar_t *)GlobalAlloc(GPTR,len*sizeof(*temp)); + l.hwndOwner = hwndDlg; + extern wchar_t* GetFilterListString(void); + l.lpstrFilter = GetFilterListString();//L"iTunes XML Library\0*.xml\0\0"; // IDS_ITUNES_XML_LIBRARY + l.lpstrFile = temp; + l.nMaxFile = len-1; + l.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_IMPORT_DATABASE); + l.lpstrDefExt = L""; + l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();; + l.Flags = OFN_HIDEREADONLY|OFN_EXPLORER; + if(GetOpenFileName(&l)) + { + wchar_t newCurPath[MAX_PATH] = {0}; + GetCurrentDirectoryW(MAX_PATH, newCurPath); + WASABI_API_APP->path_setWorkingPath(newCurPath); + SetCurrentDirectoryW(oldCurPath); + *file = temp; + return 1; + } + SetCurrentDirectoryW(oldCurPath); + return 0; +} + +// ----------------------------------------------------------------------- +// import an iTunes XML library into gen_ml +// ----------------------------------------------------------------------- +void importDatabase() +{ + StringW file; + // pick an inputfile + if (pickFile(plugin.hwndLibraryParent, &file)) + { + importAPI.ImportFromFile(plugin.hwndLibraryParent, file.getValue()); + // TODO ideally should only do this if the current view is from ml_local + PostMessage(plugin.hwndLibraryParent, WM_USER + 30, 0, 0); + } +} + +// ----------------------------------------------------------------------- +int only_itunes = -1; // ask + +// ----------------------------------------------------------------------- +// ask the user if we should also write files unsupported by iTunes +// ----------------------------------------------------------------------- +static BOOL CALLBACK exporttype_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + only_itunes = 0; + setDialogIcon(hwndDlg); + + CheckDlgButton(hwndDlg, IDC_RADIO_ALLFILES, TRUE); + CheckDlgButton(hwndDlg, IDC_RADIO_ONLYSUPPORTED, FALSE); + SetForegroundWindow(hwndDlg); + return 0; + } + + case WM_COMMAND: { + int wID = LOWORD(wParam); + switch (wID) { + case IDOK: + case IDCANCEL: + EndDialog(hwndDlg, wID); + SetForegroundWindow(export_wnd); + break; + case IDC_RADIO_ONLYSUPPORTED: + only_itunes = 1; + break; + case IDC_RADIO_ALLFILES: + only_itunes = 0; + break; + } + } + return 0; + } + return 0; +} + +// ----------------------------------------------------------------------- +// export status window proc +// ----------------------------------------------------------------------- +static BOOL CALLBACK export_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + export_wnd = hwndDlg; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + + // init it + ShowWindow(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), SW_SHOWNORMAL); + ShowWindow(GetDlgItem(hwndDlg, IDC_TRACKS), SW_SHOWNORMAL); + + SetWindowText(hwndDlg,WASABI_API_LNGSTRINGW(IDS_EXPORTING_DATABASE)); + SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, 0, 0); + + setDialogIcon(hwndDlg); + + SetTimer(hwndDlg, 666, 250, 0); + SetForegroundWindow(hwndDlg); + return 0; + } + + case WM_TIMER: + if (wParam == 666) + { + int *progress = (int *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (progress[0] == -333) + { + // show "saving xml" + ShowWindow(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_TRACKS), SW_HIDE); + SetDlgItemText(hwndDlg,IDC_PROCESSING_STATE,WASABI_API_LNGSTRINGW(IDS_WRITINGING_XML)); + ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSING_STATE), SW_SHOWNORMAL); + } + else if (progress[0] == -666) + { + KillTimer(hwndDlg, 666); + EndDialog(hwndDlg, 0); + } + else + { + // display progress + SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, (int)((double)progress[0] / progress[1] * 100.0), 0); + SetDlgItemText(hwndDlg, IDC_TRACKS, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TRACKS_EXPORTED_X), progress[0])); + } + } + break; + + case WM_COMMAND: { + int wID = LOWORD(wParam); + switch (wID) { + case IDOK: + case IDCANCEL: + EndDialog(hwndDlg, wID); + break; + } + } + return 0; + } + return 0; +} + +// ----------------------------------------------------------------------- +// returns 1 if str ends with e +// ----------------------------------------------------------------------- +int endsIn(const wchar_t *str, const wchar_t *e) { + return !_wcsicmp(str+wcslen(str)-wcslen(e), e); +} + +static DWORD CALLBACK ExportThread(LPVOID param) +{ + WASABI_API_DIALOGBOXPARAMW(IDD_INFODIALOG, NULL, export_dlgproc, (LPARAM)param); + return 0; +} + +// ----------------------------------------------------------------------- +// exports an iTunes XML library from gen_ml's database +// ----------------------------------------------------------------------- +void exportDatabase() { + // create an iTunes XML library writer + iTunesXmlWrite w; + + // pick an output file + if (w.pickFile(plugin.hwndLibraryParent)) { + // if a file unsupported by iTunes is about to be exported, ask confirmation + only_itunes = -1; + + // create status window + int progress[2] = {0}; + DWORD threadId = 0; + HANDLE exportThread = CreateThread(0, 0, ExportThread, progress, 0, &threadId); + + // create an iTunes DB in memory, start with a root key + plistKey *rootKey = new plistKey(L"root"); + // add a dictionary to it + plistDict *rootDict = new plistDict(); + rootKey->setData(rootDict); + + // add a few useless fields + rootDict->addKey(new plistKey(L"Major Version", new plistInteger(1))); + rootDict->addKey(new plistKey(L"Minor Version", new plistInteger(1))); + rootDict->addKey(new plistKey(L"Application Version", new plistString(L"7.6.1"))); // we pretend to be iTunes 7.6.1 + + // create the Tracks key and its dictionary of tracks + plistDict *dict_tracks = new plistDict(); + rootDict->addKey(new plistKey(L"Tracks", dict_tracks)); + + // run an empty query, so we get all items in the db + mlQueryStructW query; + query.query = L""; + query.max_results = 0; + query.results.Size = 0; + query.results.Items = NULL; + query.results.Alloc = 0; + + SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&query, ML_IPC_DB_RUNQUERYW); + + // my, what a big number of items... + progress[1] = query.results.Size; + + // ... enumerate them + for (int x = 0; x < progress[1]; x ++) { + // take the xth item + itemRecordW ir = query.results.Items[x]; + + // check if it's supported by iTunes, if we've already answered the question, use last answer + if ((only_itunes > 0 || only_itunes < 0) && (!endsIn(ir.filename, L".mp3") && !endsIn(ir.filename, L".m4a") && !endsIn(ir.filename, L".wav") && !endsIn(ir.filename, L".aiff"))) { + if (only_itunes < 0) { + // prompt user, should we export files unsupported by iTunes ? + WASABI_API_DIALOGBOXW(IDD_EXPORTTYPE, plugin.hwndLibraryParent, exporttype_dlgproc); + } + // if not, continue with the next item + if (only_itunes) continue; + } + + // create a track key, its name is the number of the track (not counting skipped tracks) + plistKey *key_track = new plistKey(StringPrintfW(L"%d", progress[0])); + dict_tracks->addKey(key_track); + + // give it a dictionary to hold its properties + plistDict *dict_track = new plistDict(); + key_track->setData(dict_track); + + // create the properties as needed + dict_track->addKey(new plistKey(L"Track ID", new plistInteger(progress[0]))); + if (ir.title) dict_track->addKey(new plistKey(L"Name", new plistString(ir.title))); + if (ir.artist) dict_track->addKey(new plistKey(L"Artist", new plistString(ir.artist))); + if (ir.albumartist) dict_track->addKey(new plistKey(L"Album Artist", new plistString(ir.albumartist))); + if (ir.album) dict_track->addKey(new plistKey(L"Album", new plistString(ir.album))); + if (ir.genre) dict_track->addKey(new plistKey(L"Genre", new plistString(ir.genre))); + if (ir.comment) dict_track->addKey(new plistKey(L"Comments", new plistString(ir.comment))); + dict_track->addKey(new plistKey(L"Kind", new plistString(L"MPEG audio file"))); + + // changed in 5.64 to use the 'realsize' if it's available, otherwise map to filesize scaled to bytes (stored as kb otherwise) + const wchar_t *realsize = getRecordExtendedItem(&ir, L"realsize"); + if (realsize) dict_track->addKey(new plistKey(L"Size", new plistInteger(_wtoi64(realsize)))); + else if (ir.filesize > 0) dict_track->addKey(new plistKey(L"Size", new plistInteger(ir.filesize * 1024))); + + if (ir.length >= 0) dict_track->addKey(new plistKey(L"Total Time", new plistInteger(ir.length * 1000))); + if (ir.track >= 0) dict_track->addKey(new plistKey(L"Track Number", new plistInteger(ir.track))); + if (ir.year >= 0) dict_track->addKey(new plistKey(L"Year", new plistInteger(ir.year))); + if (ir.filetime> 0) dict_track->addKey(new plistKey(L"Date Modified", new plistDate((time_t)ir.filetime))); + if (ir.lastupd> 0) dict_track->addKey(new plistKey(L"Date Added", new plistDate((time_t)ir.lastupd))); + //if (ir.lastplay> 0) dict_track->addKey(new plistKey(L"Play Date", new plistInteger((time_t)ir.lastplay))); + if (ir.lastplay > 0) dict_track->addKey(new plistKey(L"Play Date UTC", new plistDate((time_t)ir.lastplay))); + if (ir.bitrate> 0) dict_track->addKey(new plistKey(L"Bit Rate", new plistInteger(ir.bitrate))); + if (ir.playcount> 0) dict_track->addKey(new plistKey(L"Play Count", new plistInteger(ir.playcount))); + if (ir.rating> 0) dict_track->addKey(new plistKey(L"Rating", new plistInteger(ir.rating * 20))); + if (ir.composer) dict_track->addKey(new plistKey(L"Composer", new plistString(ir.composer))); + if (ir.publisher) dict_track->addKey(new plistKey(L"Publisher", new plistString(ir.publisher))); + if (ir.type == 1) dict_track->addKey(new plistKey(L"Has Video", new plistInteger(1))); + if (ir.disc> 0) dict_track->addKey(new plistKey(L"Disc Number", new plistInteger(ir.disc))); + if (ir.discs> 0) dict_track->addKey(new plistKey(L"Disc Count", new plistInteger(ir.discs))); + if (ir.tracks > 0) dict_track->addKey(new plistKey(L"Track Count", new plistInteger(ir.tracks))); + if (ir.bpm > 0) dict_track->addKey(new plistKey(L"BPM", new plistInteger(ir.bpm))); + const wchar_t *category = getRecordExtendedItem(&ir, L"category"); + if (category) dict_track->addKey(new plistKey(L"Grouping", new plistString(category))); + const wchar_t *producer = getRecordExtendedItem(&ir, L"producer"); + if (producer) dict_track->addKey(new plistKey(L"Producer", new plistString(producer))); + const wchar_t *director = getRecordExtendedItem(&ir, L"director"); + if (director) dict_track->addKey(new plistKey(L"Director", new plistString(director))); + const wchar_t *width = getRecordExtendedItem(&ir, L"width"); + if (width) dict_track->addKey(new plistKey(L"Video Width", new plistInteger(_wtoi64(width)))); + const wchar_t *height = getRecordExtendedItem(&ir, L"height"); + if (height) dict_track->addKey(new plistKey(L"Video Height", new plistInteger(_wtoi64(height)))); + // convert the filename's backslashes to slashes + StringW s = ir.filename; + + if (WCSNICMP(s, L"http://", 7)) + { + wchar_t *val = s.getNonConstVal(); + // TODO: we could do this with less malloc usage + AutoChar utf8(val, CP_UTF8); + String encoded = (const char *)utf8; + Url::encode(encoded, 0, URLENCODE_EXCLUDEALPHANUM|URLENCODE_EXCLUDESLASH, Url::URLENCODE_STYLE_PERCENT); + + s = StringPrintfW(L"%s%s", ITUNES_FILENAME_HEADER, AutoWide(encoded, CP_UTF8)); + } + + // done + dict_track->addKey(new plistKey(L"Location", new plistString(s))); + dict_track->addKey(new plistKey(L"File Folder Count", new plistInteger(-1))); + dict_track->addKey(new plistKey(L"Library Folder Count", new plistInteger(-1))); + + // we have one more item in our exported db + progress[0]++; + } + + // show "saving xml" + progress[0]=-333; + + // free query results + SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&query, ML_IPC_DB_FREEQUERYRESULTSW); + + // save the xml + w.saveXml(rootKey); + + // done + progress[0]=-666; + if (exportThread) + { + WaitForSingleObject(exportThread, INFINITE); + CloseHandle(exportThread); + } + + // destroy the db, this deletes all the children too + delete rootKey; + } +} + +//------------------------------------------------------------------------
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/importer.cpp b/Src/Plugins/Library/ml_impex/importer.cpp new file mode 100644 index 00000000..e6b5c271 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/importer.cpp @@ -0,0 +1,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; + } + } +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/importer.h b/Src/Plugins/Library/ml_impex/importer.h new file mode 100644 index 00000000..fbed11b6 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/importer.h @@ -0,0 +1,66 @@ +#pragma once +#include "../plist/types.h" +#include "../../General/gen_ml/ml.h" + +// header for local filenames +#define ITUNES_FILENAME_HEADER L"file://localhost/" + +enum +{ + IT_TRACKID=0, + IT_NAME, + IT_ARTIST, + IT_ALBUMARTIST, + IT_ALBUM, + IT_GENRE, + IT_COMMENTS, + IT_KIND, + IT_SIZE, + IT_TOTALTIME, + IT_TRACKNUM, + IT_TRACKCOUNT, + IT_YEAR, + IT_DATEMODIFIED, + IT_DATEADDED, + IT_BITRATE, + IT_RATING, + IT_SAMPLERATE, + IT_LOCATION, + IT_FOLDERCOUNT, + IT_LIBFOLDERCOUNT, + IT_PLAYCOUNT, + IT_PLAYDATE, + IT_PLAYDATE_UTC, + IT_COMPOSER, + IT_PUBLISHER, + IT_DISCNUMBER, + IT_DISCCOUNT, + IT_BPM, + IT_HAS_VIDEO, + IT_GROUPING, + IT_PRODUCER, + IT_DIRECTOR, + IT_ARTWORK_COUNT, + IT_PERSISTENT_ID, + IT_TRACK_TYPE, + IT_HD, + IT_VIDEO_WIDTH, + IT_VIDEO_HEIGHT, + IT_MOVIE, + IT_RELEASE_DATE, + IT_NORMALIZATION, + IT_SORTNAME, + IT_PURCHASED, + IT_ITUNESU, + IT_SKIPCOUNT, + IT_SKIPDATE, + IT_SORTALBUM, + IT_SORTCOMPOSER, + IT_PART_OF_GAPLESS_ALBUM, + IT_COMPILATION, + IT_SORT_ALBUM_ARTIST, + IT_SORT_ARTIST, +}; +void Importer_AddKeyToItemRecord(const plistKey *prop, itemRecordW &ir); +int ImportPlaylists(HWND parent, const wchar_t *library_file); +void setDialogIcon(HWND hwndDlg);
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp b/Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp new file mode 100644 index 00000000..b4f86352 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------ +// +// iTunes XML Library Writer +// Copyright © 2003-2014 Winamp SA +// +//------------------------------------------------------------------------ + +#include <windows.h> +#include <commdlg.h> +#include <api/xml/xmlwrite.h> +#include <bfc/string/url.h> +#include "itunesxmlwrite.h" +#include "../plist/types.h" +#include "api__ml_impex.h" +#include "resource.h" + +//------------------------------------------------------------------------ +iTunesXmlWrite::iTunesXmlWrite() { +} + +//------------------------------------------------------------------------ +iTunesXmlWrite::~iTunesXmlWrite() { +} + +//------------------------------------------------------------------------ +int iTunesXmlWrite::pickFile(HWND hwndDlg, const wchar_t *title) +{ + wchar_t oldCurPath[MAX_PATH] = {0}; + GetCurrentDirectoryW(MAX_PATH, oldCurPath); + + OPENFILENAME l={sizeof(l),}; + wchar_t *temp; + const int len=256*1024-128; + + temp = (wchar_t *)GlobalAlloc(GPTR,len*sizeof(*temp)); + l.hwndOwner = hwndDlg; + //l.lpstrFilter = L"iTunes XML Library\0*.xml\0\0"; // IDS_ITUNES_XML_LIBRARY + extern wchar_t* GetFilterListString(void); + l.lpstrFilter = GetFilterListString();//L"iTunes XML Library\0*.xml\0\0"; // IDS_ITUNES_XML_LIBRARY + l.lpstrFile = temp; + l.nMaxFile = len-1; + l.lpstrTitle = title ? title : WASABI_API_LNGSTRINGW(IDS_EXPORT_DATABASE); + l.lpstrDefExt = L"xml"; + l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath(); + l.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&l)) + { + wchar_t newCurPath[MAX_PATH] = {0}; + GetCurrentDirectoryW(MAX_PATH, newCurPath); + WASABI_API_APP->path_setWorkingPath(newCurPath); + SetCurrentDirectoryW(oldCurPath); + file = temp; + return 1; + } + SetCurrentDirectoryW(oldCurPath); + return 0; +} + +//------------------------------------------------------------------------ +void iTunesXmlWrite::saveXml(plistKey *rootkey) { + if (file.isempty()) return; + XMLWrite w(file, L"plist version=\"1.0\"", L"plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"", 1); + writeData(&w, rootkey->getData()); +} + +//------------------------------------------------------------------------ +void iTunesXmlWrite::writeData(XMLWrite *writer, plistData *data) +{ + switch (data->getType()) + { + case PLISTDATA_KEY: + { + plistKey *key = (plistKey *)data; + writer->writeAttrib(data->getTypeString(), data->getString(), key->getData()->getType() == PLISTDATA_DICT || key->getData()->getType() == PLISTDATA_ARRAY || key->getData()->getType() == PLISTDATA_RAW); + writeData(writer, key->getData()); + break; + } + case PLISTDATA_DICT: + { + plistDict *dict = (plistDict *)data; + writer->pushCategory(data->getTypeString()); + for (int i=0;i<dict->getNumKeys();i++) + { + writeData(writer, dict->enumKey(i)); + } + writer->popCategory(); + break; + } + case PLISTDATA_ARRAY: + { + plistArray *array = (plistArray *)data; + writer->pushCategory(data->getTypeString()); + for (int i=0;i<array->getNumItems();i++) { + writeData(writer, array->enumItem(i)); + } + writer->popCategory(); + break; + } + case PLISTDATA_INTEGER: + case PLISTDATA_DATE: + case PLISTDATA_RAW: + { + const wchar_t *str = data->getString(); + if (str && *str) + { + writer->writeAttrib(data->getTypeString(), str, 1, 0); + } + } + break; + case PLISTDATA_STRING: + { + const wchar_t *str = data->getString(); + if (str && *str) + { + // not pretty but it'll strip out control characters + // in the 0 - 31 range that will cause import issues + wchar_t *temp = 0; + int len = (int)wcslen(str) + 1; + temp = (wchar_t*)calloc(len, sizeof(wchar_t)); + wchar_t *ptr = temp; + while(str && *str) + { + int chr = *str; + if (chr >= 0 && chr <= 31) + { + if(chr == 9 || chr == 10 || chr == 13) + { + *ptr = *str; + ptr++; + } + } + else + { + *ptr = *str; + ptr++; + } + str = CharNextW(str); + } + *ptr=0; + writer->writeAttrib(data->getTypeString(), (temp ? temp : str), 1, 0); + if (temp) free(temp); + } + } + break; + case PLISTDATA_BOOLEAN: + { + //plistBoolean *booldata = (plistBoolean *)data; + writer->writeAttribEmpty(data->getString(), 1, 0); + } + break; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/itunesxmlwrite.h b/Src/Plugins/Library/ml_impex/itunesxmlwrite.h new file mode 100644 index 00000000..caf47453 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/itunesxmlwrite.h @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------ +// +// iTunes XML Library Writer +// Copyright © 2003-2014 Winamp SA +// +//------------------------------------------------------------------------ + +#ifndef _ITUNESXMLWRITE_H +#define _ITUNESXMLWRITE_H + +class plistKey; +class XMLWrite; +class plistData; +#include <bfc/string/stringw.h> +//------------------------------------------------------------------------ + +class iTunesXmlWrite { +public: + iTunesXmlWrite(); + virtual ~iTunesXmlWrite(); + + int pickFile(HWND hwndDlg, const wchar_t *title=NULL); + void saveXml(plistKey *rootkey); + + void writeData(XMLWrite *writer, plistData *data); + +private: + StringW file; +}; + +#endif + +//------------------------------------------------------------------------
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/ml_impex.rc b/Src/Plugins/Library/ml_impex/ml_impex.rc new file mode 100644 index 00000000..4d745564 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ml_impex.rc @@ -0,0 +1,144 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INFODIALOG DIALOGEX 0, 0, 188, 47 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "Progress1",IDC_PROGRESS_PERCENT,"msctls_progress32",PBS_SMOOTH | NOT WS_VISIBLE | WS_BORDER,7,7,174,12 + CTEXT "",IDC_TRACKS,7,27,174,8,NOT WS_VISIBLE + CTEXT "",IDC_PROCESSING_STATE,35,19,117,8 +END + +IDD_EXPORTTYPE DIALOGEX 0, 0, 229, 71 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Unsupported File Formats" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Some files in your database are not supported by iTunes.",IDC_STATIC,9,11,184,8 + CONTROL "Export all files anyway.",IDC_RADIO_ALLFILES,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,15,24,91,10 + CONTROL "Export only files supported by iTunes (mp3/aac/wav/aiff).",IDC_RADIO_ONLYSUPPORTED, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,15,35,201,10 + DEFPUSHBUTTON "OK",IDOK,172,50,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_INFODIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 40 + END + + IDD_EXPORTTYPE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 222 + TOPMARGIN, 7 + BOTTOMMARGIN, 64 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ML_IMPEX_DESC "Nullsoft Database Import / Export v%s" + 65535 "{22661553-8D22-4012-8D3B-0FF8FE57A9ED}" +END + +STRINGTABLE +BEGIN + IDS_IMPORT_DATABASE "Import Media Database..." + IDS_EXPORT_DATABASE "Export Media Database..." + IDS_TRACKS_IMPORTED_X "Tracks imported : %d" + IDS_TRACKS_EXPORTED_X "Tracks exported : %d" + IDS_ITUNES_XML_LIBRARY "iTunes XML Library" + IDS_LOADING_XML "Loading XML..." + IDS_WRITINGING_XML "Writing XML..." + IDS_IMPORTING_DATABASE "Importing Database" + IDS_EXPORTING_DATABASE "Exporting Database" + IDS_IMPORT_ITUNES_DB "Import iTunes Library" + IDS_IMPORT_ITUNES_PL "Import iTunes Playlists" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_impex/ml_impex.sln b/Src/Plugins/Library/ml_impex/ml_impex.sln new file mode 100644 index 00000000..e5e06ad5 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ml_impex.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_impex", "ml_impex.vcxproj", "{DDD08D55-B0E6-4070-9C67-39B495A0B4ED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bfc", "..\Wasabi\bfc\bfc.vcxproj", "{D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plist", "..\plist\plist.vcxproj", "{5ED1729B-EA41-4163-9506-741A8B76F625}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Debug|Win32.ActiveCfg = Debug|Win32 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Debug|Win32.Build.0 = Debug|Win32 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Debug|x64.ActiveCfg = Debug|x64 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Debug|x64.Build.0 = Debug|x64 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Release|Win32.ActiveCfg = Release|Win32 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Release|Win32.Build.0 = Release|Win32 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Release|x64.ActiveCfg = Release|x64 + {DDD08D55-B0E6-4070-9C67-39B495A0B4ED}.Release|x64.Build.0 = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.ActiveCfg = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.Build.0 = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.ActiveCfg = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.Build.0 = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.ActiveCfg = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.Build.0 = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.ActiveCfg = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.Build.0 = Release|x64 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Debug|Win32.ActiveCfg = Debug|Win32 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Debug|Win32.Build.0 = Debug|Win32 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Debug|x64.ActiveCfg = Debug|x64 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Debug|x64.Build.0 = Debug|x64 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Release|Win32.ActiveCfg = Release|Win32 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Release|Win32.Build.0 = Release|Win32 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Release|x64.ActiveCfg = Release|x64 + {5ED1729B-EA41-4163-9506-741A8B76F625}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1C93F5AD-DB79-444D-A5CE-58652C31D19F} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_impex/ml_impex.vcxproj b/Src/Plugins/Library/ml_impex/ml_impex.vcxproj new file mode 100644 index 00000000..c1cf536d --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ml_impex.vcxproj @@ -0,0 +1,317 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{DDD08D55-B0E6-4070-9C67-39B495A0B4ED}</ProjectGuid> + <RootNamespace>ml_impex</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_IMPEX_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_IMPEX_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_IMPEX_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_IMPEX_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\plist\plist.vcxproj"> + <Project>{5ed1729b-ea41-4163-9506-741a8b76f625}</Project> + <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies> + <ReferenceOutputAssembly>true</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\bfc\bfc.vcxproj"> + <Project>{d0ec862e-dddd-4f4f-934f-b75dc9062dc1}</Project> + <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies> + <ReferenceOutputAssembly>true</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp" /> + <ClCompile Include="impex.cpp" /> + <ClCompile Include="importer.cpp" /> + <ClCompile Include="ImporterAPI.cpp" /> + <ClCompile Include="ImportPlaylists.cpp" /> + <ClCompile Include="itunesxmlwrite.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__ml_impex.h" /> + <ClInclude Include="api_importer.h" /> + <ClInclude Include="importer.h" /> + <ClInclude Include="ImporterAPI.h" /> + <ClInclude Include="itunesxmlwrite.h" /> + <ClInclude Include="resource.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_impex.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/ml_impex.vcxproj.filters b/Src/Plugins/Library/ml_impex/ml_impex.vcxproj.filters new file mode 100644 index 00000000..7db52b4c --- /dev/null +++ b/Src/Plugins/Library/ml_impex/ml_impex.vcxproj.filters @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="impex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="importer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ImporterAPI.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ImportPlaylists.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="itunesxmlwrite.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__ml_impex.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api_importer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="importer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ImporterAPI.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="itunesxmlwrite.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{367262fe-81b8-41c9-a013-150093098f81}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{91f9aff7-f6fc-45c6-9c44-88bf5ec938ce}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{a20e93fc-10cf-4f90-b11f-2e91d427b1b1}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\gen_ml"> + <UniqueIdentifier>{5957e24d-fc45-47d0-8c55-b6a758580b36}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_impex.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_impex/resource.h b/Src/Plugins/Library/ml_impex/resource.h new file mode 100644 index 00000000..2b999df1 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/resource.h @@ -0,0 +1,37 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_impex.rc +// +#define IDS_IMPORT_DATABASE 1 +#define IDS_EXPORT_DATABASE 2 +#define IDS_TRACKS_IMPORTED_X 3 +#define IDS_TRACKS_EXPORTED_X 4 +#define IDS_ITUNES_XML_LIBRARY 5 +#define IDS_LOADING_XML 6 +#define IDS_WRITINGING_XML 7 +#define IDS_IMPORTING_DATABASE 8 +#define IDS_EXPORTING_DATABASE 9 +#define IDS_IMPORT_ITUNES_DB 10 +#define IDS_IMPORT_ITUNES_PL 11 +#define IDD_IMPORT 102 +#define IDD_INFODIALOG 102 +#define IDD_EXPORTTYPE 104 +#define IDC_TRACKS 1000 +#define IDC_PROGRESS_PERCENT 1001 +#define IDC_LOADING 1002 +#define IDC_PROCESSING_STATE 1002 +#define IDC_WRITING 1003 +#define IDC_RADIO_ONLYSUPPORTED 1004 +#define IDC_RADIO_ALLFILES 1005 +#define IDS_ML_IMPEX_DESC 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_impex/version.rc2 b/Src/Plugins/Library/ml_impex/version.rc2 new file mode 100644 index 00000000..80ed23e7 --- /dev/null +++ b/Src/Plugins/Library/ml_impex/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,65,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "2,65,0,0" + VALUE "InternalName", "Nullsoft Database Import / Export" + VALUE "LegalCopyright", "Copyright © 2009-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_impex.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |