ImportPlaylists.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include "api__ml_impex.h"
  2. #include "../xml/obj_xml.h"
  3. #include <map>
  4. #include <bfc/string/url.h>
  5. #include "importer.h"
  6. #include "resource.h"
  7. #include "../plist/loader.h"
  8. #include "../playlist/ifc_playlist.h"
  9. #include "../Winamp/wa_ipc.h"
  10. #include <shlwapi.h>
  11. struct iTunesFileInfo
  12. {
  13. iTunesFileInfo(const wchar_t *_filename, uint64_t _length)
  14. {
  15. filename = _wcsdup(_filename);
  16. length = _length;
  17. }
  18. ~iTunesFileInfo()
  19. {
  20. free(filename);
  21. }
  22. wchar_t *filename;
  23. uint64_t length;
  24. };
  25. typedef std::map<int64_t, iTunesFileInfo*> FilesList;
  26. extern winampMediaLibraryPlugin plugin;
  27. int Load(const wchar_t *filename, obj_xml *parser);
  28. class PlistPlaylist : public ifc_playlist
  29. {
  30. public:
  31. PlistPlaylist(const plistArray *_items, FilesList &_files) : items(_items), files(_files)
  32. {
  33. length_sum = 0;
  34. }
  35. size_t GetNumItems();
  36. size_t GetItem(size_t item, wchar_t *filename, size_t filenameCch);
  37. int GetItemLengthMilliseconds(size_t item);
  38. uint64_t length_sum;
  39. protected:
  40. RECVS_DISPATCH;
  41. const plistArray *items;
  42. FilesList &files;
  43. };
  44. size_t PlistPlaylist::GetNumItems()
  45. {
  46. return items->getNumItems();
  47. }
  48. size_t PlistPlaylist::GetItem(size_t item, wchar_t *filename, size_t filenameCch)
  49. {
  50. plistDict *item_dict = (plistDict *)items->enumItem((int)item);
  51. if (item_dict)
  52. {
  53. plistKey *id_key = item_dict->getKey(L"Track ID");
  54. if (id_key)
  55. {
  56. plistInteger *id_data = (plistInteger *)id_key->getData();
  57. if (id_data)
  58. {
  59. int64_t key = id_data->getValue();
  60. iTunesFileInfo *info = files[key];
  61. if (info)
  62. {
  63. const wchar_t *track_name = info->filename;
  64. if (track_name)
  65. {
  66. length_sum += info->length;
  67. StringCchCopyW(filename, filenameCch, track_name);
  68. return 1;
  69. }
  70. }
  71. }
  72. }
  73. }
  74. return 0;
  75. }
  76. int PlistPlaylist::GetItemLengthMilliseconds(size_t item)
  77. {
  78. plistDict *item_dict = (plistDict *)items->enumItem((int)item);
  79. if (item_dict)
  80. {
  81. plistKey *id_key = item_dict->getKey(L"Track ID");
  82. if (id_key)
  83. {
  84. plistInteger *id_data = (plistInteger *)id_key->getData();
  85. if (id_data)
  86. {
  87. int64_t key = id_data->getValue();
  88. iTunesFileInfo *info = files[key];
  89. if (info)
  90. {
  91. return (int)info->length;
  92. }
  93. }
  94. }
  95. }
  96. return 0;
  97. }
  98. #define CBCLASS PlistPlaylist
  99. START_DISPATCH;
  100. CB(IFC_PLAYLIST_GETNUMITEMS, GetNumItems)
  101. CB(IFC_PLAYLIST_GETITEM, GetItem)
  102. CB(IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds)
  103. END_DISPATCH;
  104. #undef CBCLASS
  105. static bool GetInteger(const plistDict *dict, const wchar_t *key_name, int64_t *int_val)
  106. {
  107. plistKey *key = dict->getKey(key_name);
  108. if (!key)
  109. return false;
  110. plistData *data = key->getData();
  111. if (!data)
  112. return false;
  113. if (data->getType() != PLISTDATA_INTEGER)
  114. return false;
  115. plistInteger *data_int = static_cast<plistInteger *>(data);
  116. *int_val = data_int->getValue();
  117. return true;
  118. }
  119. static bool GetString(const plistDict *dict, const wchar_t *key_name, const wchar_t **str_val)
  120. {
  121. plistKey *key = dict->getKey(key_name);
  122. if (!key)
  123. return false;
  124. plistData *data = key->getData();
  125. if (!data)
  126. return false;
  127. if (data->getType() != PLISTDATA_STRING)
  128. return false;
  129. plistString *data_str = static_cast<plistString *>(data);
  130. *str_val = data_str->getString();
  131. return true;
  132. }
  133. static bool GetArray(const plistDict *dict, const wchar_t *key_name, plistArray **array_val)
  134. {
  135. plistKey *key = dict->getKey(key_name);
  136. if (!key)
  137. return false;
  138. plistData *data = key->getData();
  139. if (!data)
  140. return false;
  141. if (data->getType() != PLISTDATA_ARRAY)
  142. return false;
  143. *array_val = static_cast<plistArray *>(data);
  144. return true;
  145. }
  146. static bool CheckDuplicatePlaylist(uint64_t playlist_id64, GUID &dup_guid)
  147. {
  148. AGAVE_API_PLAYLISTS->Lock();
  149. size_t numPlaylists = AGAVE_API_PLAYLISTS->GetCount();
  150. uint64_t compare_id64=0;
  151. for (size_t i=0;i!=numPlaylists;i++)
  152. {
  153. if (AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_iTunesID, &compare_id64, sizeof(compare_id64)) == API_PLAYLISTS_SUCCESS)
  154. {
  155. if (compare_id64 == playlist_id64)
  156. {
  157. dup_guid = AGAVE_API_PLAYLISTS->GetGUID(i);
  158. AGAVE_API_PLAYLISTS->Unlock();
  159. return true;
  160. }
  161. }
  162. }
  163. AGAVE_API_PLAYLISTS->Unlock();
  164. return false;
  165. }
  166. enum
  167. {
  168. DUPLICATE_PLAYLIST_SKIP,
  169. DUPLICATE_PLAYLIST_REPLACE,
  170. DUPLICATE_PLAYLIST_NEW,
  171. };
  172. static int PromptReplaceSkipNew(GUID &dup_guid)
  173. {
  174. /* TODO:
  175. * get name and stuff from api_playlists
  176. * we'll need an HWND for the UI
  177. * we'll need some passed-in state variable to remember "do for all" choice
  178. */
  179. return DUPLICATE_PLAYLIST_SKIP;
  180. }
  181. HINSTANCE cloud_hinst = 0;
  182. int IPC_GET_CLOUD_HINST = -1, IPC_GET_CLOUD_ACTIVE = -1;
  183. int cloudAvailable()
  184. {
  185. if (IPC_GET_CLOUD_HINST == -1) IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE);
  186. if (IPC_GET_CLOUD_ACTIVE == -1) IPC_GET_CLOUD_ACTIVE = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloudActive", IPC_REGISTER_WINAMP_IPCMESSAGE);
  187. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  188. return (/*0/*/!(!cloud_hinst || cloud_hinst == (HINSTANCE)1 || !SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))/**/);
  189. }
  190. static void AddPlaylist(plistDict *playlist, FilesList &files)
  191. {
  192. const wchar_t *name;
  193. const wchar_t *playlist_persistent_id;
  194. int64_t playlist_id;
  195. int64_t visible;
  196. plistArray *items;
  197. uint64_t playlist_id64;
  198. if (GetString(playlist, L"Name", &name)
  199. && GetArray(playlist, L"Playlist Items", &items)
  200. && GetInteger(playlist, L"Playlist ID", &playlist_id)
  201. && GetString(playlist, L"Playlist Persistent ID", &playlist_persistent_id)
  202. && (!GetInteger(playlist, L"Visible", &visible) || visible))
  203. {
  204. playlist_id64 =_wcstoui64(playlist_persistent_id, 0, 16);
  205. // see if it's already in the database
  206. GUID dup_guid; // so we know the GUID we clash with, in case we want to replace it instead of skip it
  207. if (playlist_id64 && CheckDuplicatePlaylist(playlist_id64, dup_guid))
  208. {
  209. switch(PromptReplaceSkipNew(dup_guid))
  210. {
  211. case DUPLICATE_PLAYLIST_SKIP:
  212. break;
  213. case DUPLICATE_PLAYLIST_REPLACE:
  214. // TODO
  215. break;
  216. case DUPLICATE_PLAYLIST_NEW:
  217. // TODO
  218. break;
  219. }
  220. }
  221. else
  222. {
  223. PlistPlaylist plist_playlist(items, files);
  224. const wchar_t *user_folder = WASABI_API_APP->path_getUserSettingsPath();
  225. wchar_t destination[MAX_PATH] = {0};
  226. PathCombineW(destination, user_folder, L"plugins\\ml\\playlists");
  227. wchar_t playlist_filename[MAX_PATH] = {0};
  228. StringCbPrintfW(playlist_filename, sizeof(playlist_filename), L"i_%I64u.m3u8", playlist_id);
  229. PathAppendW(destination, playlist_filename);
  230. static wchar_t ml_ini_file[MAX_PATH] = {0};
  231. if (!ml_ini_file[0]) lstrcpynW(ml_ini_file, (const wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW), MAX_PATH);
  232. size_t cloud = (cloudAvailable() ? GetPrivateProfileIntW(L"gen_ml_config", L"cloud_always", 1, ml_ini_file) : 0);
  233. AGAVE_API_PLAYLISTMANAGER->Save(destination, &plist_playlist);
  234. AGAVE_API_PLAYLISTS->Lock();
  235. int new_index = (!cloud ? (int)AGAVE_API_PLAYLISTS->AddPlaylist(destination, name) : (int)AGAVE_API_PLAYLISTS->AddCloudPlaylist(destination, name));
  236. if (new_index >= 0)
  237. {
  238. uint32_t numItems = (uint32_t)plist_playlist.GetNumItems();
  239. uint64_t totalLength = plist_playlist.length_sum/1000;
  240. AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_totalTime, &totalLength, sizeof(totalLength));
  241. AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_itemCount, &numItems, sizeof(numItems));
  242. if (cloud) AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_cloud, &cloud, sizeof(cloud));
  243. AGAVE_API_PLAYLISTS->SetInfo(new_index, api_playlists_iTunesID, &playlist_id64, sizeof(playlist_id64));
  244. }
  245. AGAVE_API_PLAYLISTS->Unlock();
  246. PostMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"ml_playlist_refresh", IPC_REGISTER_WINAMP_IPCMESSAGE));
  247. }
  248. }
  249. }
  250. void FixPath(const wchar_t *strdata, StringW &f);
  251. int ImportPlaylists(HWND parent, const wchar_t *library_file)
  252. {
  253. FilesList files;
  254. // create an XML parser
  255. obj_xml *parser=0;
  256. waServiceFactory *factory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
  257. if (factory)
  258. parser = (obj_xml *)factory->getInterface();
  259. if (parser)
  260. {
  261. // create status window
  262. //HWND hwndDlg = WASABI_API_CREATEDIALOGW(IDD_INFODIALOG,plugin.hwndLibraryParent,import_dlgproc);
  263. // init it
  264. //ShowWindow(hwndDlg, SW_NORMAL);
  265. //UpdateWindow(hwndDlg);
  266. // create an iTunes XML library reader
  267. plistLoader it;
  268. // load the XML, this creates an iTunes DB in memory, and returns the root key
  269. parser->xmlreader_open();
  270. parser->xmlreader_registerCallback(L"plist\f*", &it);
  271. Load(library_file, parser);
  272. parser->xmlreader_unregisterCallback(&it);
  273. parser->xmlreader_close();
  274. plistKey *root_key = &it;
  275. // show import progress controls
  276. //ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSING_STATE), SW_HIDE);
  277. //ShowWindow(GetDlgItem(hwndDlg, IDC_PROGRESS_PERCENT), SW_SHOWNORMAL);
  278. //ShowWindow(GetDlgItem(hwndDlg, IDC_TRACKS), SW_SHOWNORMAL);
  279. //UpdateWindow(hwndDlg);
  280. // we start at the root key
  281. if (root_key)
  282. {
  283. // the root key contains a dictionary
  284. plistData *root_dict = root_key->getData();
  285. if (root_dict && root_dict->getType() == PLISTDATA_DICT)
  286. {
  287. // that dictionary contains a number of keys, one of which contains a dictionary of tracks
  288. plistKey *tracks_key = ((plistDict*)root_dict)->getKey(L"Tracks");
  289. plistData *tracks_dict = tracks_key?tracks_key->getData():0;
  290. if (tracks_dict && tracks_dict->getType() == PLISTDATA_DICT)
  291. {
  292. // we have the tracks dictionary ...
  293. plistDict *tracks = (plistDict *)tracks_dict;
  294. int n =tracks?tracks->getNumKeys():0;
  295. // ... now enumerate tracks
  296. for (int i=0;i<n;i++)
  297. {
  298. // each track is a key in the tracks dictionary, and contains a dictionary of properties
  299. plistKey *track_key = tracks->enumKey(i);
  300. plistData *track_dict = track_key->getData();
  301. // prepare an item record
  302. if (track_dict->getType() == PLISTDATA_DICT)
  303. {
  304. // we have the track's dictionary of properties...
  305. plistDict *track = (plistDict *)track_dict;
  306. int64_t id = 0;
  307. const wchar_t *location = 0;
  308. if (GetInteger(track, L"Track ID", &id) && GetString(track, L"Location", &location))
  309. {
  310. StringW f;
  311. FixPath(location, f);
  312. int64_t length = 0;
  313. GetInteger(track, L"Total Time", &length);
  314. // done
  315. wchar_t *filename = _wcsdup(f);
  316. files[id] = new iTunesFileInfo(filename, length);
  317. // show progress
  318. //SetDlgItemText(hwndDlg, IDC_TRACKS, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TRACKS_IMPORTED_X), ++count));
  319. //SendDlgItemMessage(hwndDlg, IDC_PROGRESS_PERCENT, PBM_SETPOS, (int)((double)count/n*100.0), 0);
  320. //if (count % 10 == 0 || count == n)
  321. // UpdateWindow(hwndDlg);
  322. }
  323. }
  324. }
  325. }
  326. // ok we're done building the track list, now let's enumerate the playlists
  327. plistKey *playlists_key = ((plistDict*)root_dict)->getKey(L"Playlists");
  328. plistData *playlists_dict = playlists_key?playlists_key->getData():0;
  329. if (playlists_dict && playlists_dict->getType() == PLISTDATA_ARRAY)
  330. {
  331. plistArray *playlists = (plistArray *)playlists_dict;
  332. int n =playlists?playlists->getNumItems():0;
  333. // ... now enumerate playlists
  334. for (int i=0;i<n;i++)
  335. {
  336. // each playlist is a key in the playlists dictionary, and contains a dictionary of properties
  337. plistData *playlist_dict = playlists->enumItem(i);
  338. if (playlist_dict->getType() == PLISTDATA_DICT)
  339. {
  340. // we have the playlist's dictionary of properties...
  341. plistDict *playlist = (plistDict *)playlist_dict;
  342. AddPlaylist(playlist, files);
  343. }
  344. }
  345. }
  346. }
  347. }
  348. //DestroyWindow(hwndDlg);
  349. factory->releaseInterface(parser);
  350. }
  351. else
  352. return DISPATCH_FAILURE;
  353. FilesList::iterator itr;
  354. for (itr = files.begin(); itr!= files.end(); itr++)
  355. {
  356. iTunesFileInfo *info = itr->second;
  357. delete info;
  358. }
  359. return DISPATCH_SUCCESS;
  360. }