metadata_utils.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. #include "main.h"
  2. #include "DeviceView.h"
  3. #include "metadata_utils.h"
  4. #include "../nu/sort.h"
  5. #include <shlwapi.h>
  6. #include <strsafe.h>
  7. #include "api__ml_pmp.h"
  8. #define METADATASTRATAGYSWITCH 500
  9. filenameMap **filenameMapping;
  10. int filenameMapLen;
  11. static INT_PTR CALLBACK findingMetadata2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) {
  12. static int i;
  13. static int added;
  14. switch(uMsg) {
  15. case WM_INITDIALOG:
  16. SendDlgItemMessage(hwndDlg, IDC_METADATAPROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, filenameMapLen));
  17. i=0;
  18. added=0;
  19. SetTimer(hwndDlg, 1, 10, NULL);
  20. if (FALSE != CenterWindow(hwndDlg, (HWND)lParam))
  21. SendMessage(hwndDlg, DM_REPOSITION, 0, 0L);
  22. SetForegroundWindow(hwndDlg);
  23. return 0;
  24. case WM_TIMER:
  25. if(wParam == 1) {
  26. KillTimer(hwndDlg, 1);
  27. filenameMap **map = filenameMapping;
  28. int len = filenameMapLen;
  29. for(; i < len; i++) {
  30. if (i % 25 == 0) SendDlgItemMessage(hwndDlg, IDC_METADATAPROGRESS, PBM_SETPOS, i, 0);
  31. itemRecordW *result = AGAVE_API_MLDB->GetFile(map[i]->fn);
  32. map[i]->ice = (itemRecordW*)calloc(sizeof(itemRecordW), 1);
  33. if (result) {
  34. copyRecord(map[i]->ice, result);
  35. AGAVE_API_MLDB->FreeRecord(result);
  36. if(i % 200 == 0) {
  37. i++;
  38. PostMessage(hwndDlg, WM_TIMER, 1, 0);
  39. return 0;
  40. }
  41. } else {
  42. filenameToItemRecord(map[i]->fn, map[i]->ice); // ugh. Disk intensive.
  43. SendMessage(plugin.hwndWinampParent, WM_ML_IPC, (WPARAM)map[i]->ice, ML_IPC_DB_ADDORUPDATEITEMW);
  44. added++;
  45. i++;
  46. PostMessage(hwndDlg, WM_TIMER, 1, 0);
  47. return 0;
  48. }
  49. }
  50. if (added) SendMessage(plugin.hwndWinampParent,WM_ML_IPC,0,ML_IPC_DB_SYNCDB);
  51. EndDialog(hwndDlg,0);
  52. }
  53. break;
  54. }
  55. return 0;
  56. }
  57. void mapFilesToItemRecords(filenameMap ** map0, int len, HWND centerWindow) {
  58. filenameMapping = map0;
  59. filenameMapLen = len;
  60. if (filenameMapLen > 0)
  61. WASABI_API_DIALOGBOXPARAMW(IDD_GETTINGMETADATA,plugin.hwndWinampParent, findingMetadata2_dlgproc, (LPARAM)centerWindow);
  62. }
  63. C_ItemList * fileListToItemRecords(wchar_t** files,int l, HWND centerWindow) {
  64. filenameMap ** map = (filenameMap **)calloc(l, sizeof(void*));
  65. filenameMap * m = (filenameMap *)calloc(l, sizeof(filenameMap));
  66. for(int i=0; i<l; i++) {
  67. map[i] = &m[i];
  68. map[i]->fn = files[i];
  69. }
  70. mapFilesToItemRecords(map, l, centerWindow);
  71. C_ItemList * out = new C_ItemList;
  72. for(int i=0; i<l; i++)
  73. out->Add(map[i]->ice);
  74. free(m);
  75. free(map);
  76. return out;
  77. }
  78. C_ItemList * fileListToItemRecords(C_ItemList * fileList, HWND centerWindow) {
  79. return fileListToItemRecords((wchar_t**)fileList->GetAll(),fileList->GetSize(), centerWindow);
  80. }
  81. typedef struct
  82. {
  83. songid_t songid;
  84. Device * dev;
  85. } SortSongItem;
  86. #define RETIFNZ(v) { int zz = (v); if(zz) return zz; }
  87. #define CMPFIELDS(x) { x(a,bufa,256); x(b,bufb,256); int v = lstrcmpiW(bufa,bufb); if(v) return v; }
  88. int __fastcall compareSongs(const void *elem1, const void *elem2, const void *context) {
  89. songid_t a = *(songid_t*)elem1;
  90. songid_t b = *(songid_t*)elem2;
  91. if(a == b) return 0;
  92. Device * dev = (Device *)context;
  93. wchar_t bufa[256] = {0};
  94. wchar_t bufb[256] = {0};
  95. CMPFIELDS(dev->getTrackArtist)
  96. CMPFIELDS(dev->getTrackAlbum)
  97. CMPFIELDS(dev->getTrackTitle)
  98. int t1 = dev->getTrackTrackNum(a);
  99. int t2 = dev->getTrackTrackNum(b);
  100. if(t1>0 && t2>0) RETIFNZ(t1 - t2)
  101. return 0;
  102. }
  103. #undef CMPFIELDS
  104. static __forceinline int strcmp_nullok(wchar_t * x,wchar_t * y) {
  105. if(!x) x=L"";
  106. if(!y) y=L"";
  107. return lstrcmpiW(x,y);
  108. }
  109. int compareItemRecordAndSongId(itemRecordW * item, songid_t song, Device *dev)
  110. {
  111. wchar_t buf[2048] = {0};
  112. dev->getTrackArtist(song,buf,sizeof(buf)/sizeof(wchar_t));
  113. RETIFNZ(strcmp_nullok(buf,item->artist));
  114. dev->getTrackAlbum(song,buf,sizeof(buf)/sizeof(wchar_t));
  115. RETIFNZ(strcmp_nullok(buf,item->album));
  116. dev->getTrackTitle(song,buf,sizeof(buf)/sizeof(wchar_t));
  117. RETIFNZ(strcmp_nullok(buf,item->title));
  118. int t = dev->getTrackTrackNum(song);
  119. if(item->track>0 && t>0) RETIFNZ(t - item->track);
  120. return 0;
  121. }
  122. int compareItemRecords(itemRecordW * a, itemRecordW * b) {
  123. if(a == b) return 0;
  124. RETIFNZ(lstrcmpiW(a->artist?a->artist:L"",b->artist?b->artist:L""));
  125. RETIFNZ(lstrcmpiW(a->album?a->album:L"",b->album?b->album:L""));
  126. RETIFNZ(lstrcmpiW(a->title?a->title:L"",b->title?b->title:L""));
  127. if(a->track>0 && b->track>0) RETIFNZ(a->track - b->track);
  128. return 0;
  129. }
  130. static int sortfunc_ItemRecords_map(const void *elem1, const void *elem2) {
  131. PlaylistAddItem *a = *(PlaylistAddItem **)elem1;
  132. PlaylistAddItem *b = *(PlaylistAddItem **)elem2;
  133. return compareItemRecords(a->item,b->item);
  134. }
  135. static int sortfunc_ItemRecords(const void *elem1, const void *elem2) {
  136. itemRecordW *a = *(itemRecordW **)elem1;
  137. itemRecordW *b = *(itemRecordW **)elem2;
  138. return compareItemRecords(a,b);
  139. }
  140. #undef RETIFNZ
  141. /* Gay Venn Diagram(tm) explaining ProcessDatabaseDifferences. Leave arguments NULL if not required
  142. ml device
  143. /------\ /------\
  144. / X \
  145. / / \<-------\---songsInML and itemRecordsOnDevice
  146. \ \ / /
  147. \ X /
  148. \------/ \------/
  149. ^ ^--------songsNotInML
  150. |--itemRecordsNotOnDevice
  151. */
  152. // Runs in O(nlogn) of largest list
  153. void ProcessDatabaseDifferences(Device * dev, C_ItemList * ml0,C_ItemList * itemRecordsOnDevice, C_ItemList * itemRecordsNotOnDevice, C_ItemList * songsInML, C_ItemList * songsNotInML) {
  154. C_ItemList device2;
  155. C_ItemList *device0=&device2;
  156. int l = dev->getPlaylistLength(0);
  157. for(int i=0; i<l; i++) device0->Add((void*)dev->getPlaylistTrack(0,i));
  158. qsort(ml0->GetAll(),ml0->GetSize(),sizeof(void*),sortfunc_ItemRecords);
  159. nu::qsort(device0->GetAll(), device0->GetSize(), sizeof(void*), dev, compareSongs);
  160. C_ItemList *ml = new C_ItemList;
  161. C_ItemList *device = new C_ItemList;
  162. int i,j;
  163. {
  164. itemRecordW * lastice = NULL;
  165. songid_t lastsong = NULL;
  166. for(i=0; i<ml0->GetSize(); i++) {
  167. itemRecordW * it = (itemRecordW*)ml0->Get(i);
  168. if(lastice) if(compareItemRecords(lastice,it)==0) continue;
  169. ml->Add(it);
  170. lastice = it;
  171. }
  172. for(i=0; i<device0->GetSize(); i++) {
  173. songid_t song = (songid_t)device0->Get(i);
  174. if(lastsong) if(compareSongs((void*)&song,(void*)&lastsong, dev)==0) continue;
  175. device->Add((void*)song);
  176. lastsong = song;
  177. }
  178. }
  179. i=0,j=0;
  180. int li = device->GetSize();
  181. int lj = ml->GetSize();
  182. while(i<li && j<lj) {
  183. itemRecordW * it = (itemRecordW*)ml->Get(j);
  184. songid_t song = (songid_t)device->Get(i);
  185. int cmp = compareItemRecordAndSongId(it,song, dev);
  186. if(cmp == 0) { // song on both
  187. if(itemRecordsOnDevice) itemRecordsOnDevice->Add(it);
  188. if(songsInML) songsInML->Add((void*)song);
  189. i++;
  190. j++;
  191. }
  192. else if(cmp > 0) { //song in ml and not on device
  193. if(itemRecordsNotOnDevice) itemRecordsNotOnDevice->Add(it);
  194. j++;
  195. }
  196. else { // song on device but not in ML
  197. if(songsNotInML) songsNotInML->Add((void*)song);
  198. i++;
  199. }
  200. }
  201. // any leftovers?
  202. if(songsNotInML) while(i<li) {
  203. songid_t song = (songid_t)device->Get(i++);
  204. songsNotInML->Add((void*)song);
  205. }
  206. if(itemRecordsNotOnDevice) while(j<lj) {
  207. itemRecordW * it = (itemRecordW *)ml->Get(j++);
  208. itemRecordsNotOnDevice->Add(it);
  209. }
  210. delete ml; delete device;
  211. }
  212. void MapItemRecordsToSongs(Device * dev, PlaylistAddItem ** map, int len, C_ItemList * itemRecordsNotOnDevice) {
  213. C_ItemList device;
  214. int l = dev->getPlaylistLength(0);
  215. int i;
  216. for(i=0; i<l; i++) device.Add((void*)dev->getPlaylistTrack(0,i));
  217. qsort(map,len,sizeof(void*),sortfunc_ItemRecords_map);
  218. nu::qsort(device.GetAll(),device.GetSize(),sizeof(void*),dev,compareSongs);
  219. int j=0;
  220. i=0;
  221. int li = device.GetSize();
  222. int lj = len;
  223. while(i<li && j<lj) {
  224. PlaylistAddItem* p = map[j];
  225. songid_t s = (songid_t)device.Get(i);
  226. int cmp = compareItemRecordAndSongId(p->item,s, dev);
  227. if(cmp == 0) {
  228. p->songid = s;
  229. j++;
  230. }
  231. else if(cmp > 0) { j++; if(itemRecordsNotOnDevice) itemRecordsNotOnDevice->Add(p->item); }
  232. else i++;
  233. }
  234. }
  235. void ProcessDatabaseDifferences(Device * dev, itemRecordListW * ml,C_ItemList * itemRecordsOnDevice, C_ItemList * itemRecordsNotOnDevice, C_ItemList * songsInML, C_ItemList * songsNotInML) {
  236. if(!ml) return;
  237. C_ItemList ml_list;
  238. for(int i=0; i < ml->Size; i++) ml_list.Add(&ml->Items[i]);
  239. ProcessDatabaseDifferences(dev,&ml_list,itemRecordsOnDevice,itemRecordsNotOnDevice,songsInML,songsNotInML);
  240. }
  241. typedef struct { songid_t song; Device * dev; const wchar_t * filename; } tagItem;
  242. static wchar_t * tagFunc(const wchar_t * tag, void * p) { //return 0 if not found, -1 for empty tag
  243. tagItem * s = (tagItem *)p;
  244. int len = 2048;
  245. wchar_t * buf = (wchar_t *)calloc(len, sizeof(wchar_t));
  246. if (buf)
  247. {
  248. if (!_wcsicmp(tag, L"artist")) s->dev->getTrackArtist(s->song,buf,len);
  249. else if (!_wcsicmp(tag, L"album")) s->dev->getTrackAlbum(s->song,buf,len);
  250. else if (!_wcsicmp(tag, L"title")) s->dev->getTrackTitle(s->song,buf,len);
  251. else if (!_wcsicmp(tag, L"genre")) s->dev->getTrackGenre(s->song,buf,len);
  252. else if (!_wcsicmp(tag, L"year")) wsprintf(buf,L"%d",s->dev->getTrackYear(s->song));
  253. else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track")) wsprintf(buf,L"%d",s->dev->getTrackTrackNum(s->song));
  254. else if (!_wcsicmp(tag, L"discnumber")) wsprintf(buf,L"%d",s->dev->getTrackDiscNum(s->song));
  255. else if (!_wcsicmp(tag, L"bitrate")) wsprintf(buf,L"%d",s->dev->getTrackBitrate(s->song));
  256. else if (!_wcsicmp(tag, L"filename")) lstrcpyn(buf,s->filename,len);
  257. else if (!_wcsicmp(tag, L"albumartist")) s->dev->getTrackAlbumArtist(s->song,buf,len);
  258. else if (!_wcsicmp(tag, L"composer")) s->dev->getTrackComposer(s->song,buf,len);
  259. else if (!_wcsicmp(tag, L"publisher")) s->dev->getTrackPublisher(s->song,buf,len);
  260. else if (!_wcsicmp(tag, L"mime")) s->dev->getTrackMimeType(s->song,buf,len);
  261. }
  262. return buf;
  263. }
  264. static void tagFreeFunc(wchar_t *tag, void *p) { if(tag) free(tag); }
  265. static time_t FileTimeToUnixTime(FILETIME *ft)
  266. {
  267. ULARGE_INTEGER end;
  268. memcpy(&end,ft,sizeof(end));
  269. end.QuadPart -= 116444736000000000;
  270. end.QuadPart /= 10000000; // 100ns -> seconds
  271. return (time_t)end.QuadPart;
  272. }
  273. static __int64 FileSize64(HANDLE file)
  274. {
  275. LARGE_INTEGER position;
  276. position.QuadPart=0;
  277. position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
  278. if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
  279. return INVALID_FILE_SIZE;
  280. else
  281. return position.QuadPart;
  282. }
  283. void GetFileSizeAndTime(const wchar_t *filename, __int64 *file_size, time_t *file_time)
  284. {
  285. WIN32_FILE_ATTRIBUTE_DATA file_data;
  286. if (GetFileAttributesExW(filename, GetFileExInfoStandard, &file_data) == FALSE)
  287. {
  288. // GetFileAttributesEx failed. that sucks, let's try something else
  289. HANDLE hFile=CreateFileW(filename,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
  290. if (hFile != INVALID_HANDLE_VALUE)
  291. {
  292. FILETIME lt;
  293. if (GetFileTime(hFile,NULL,NULL,&lt))
  294. {
  295. *file_time=FileTimeToUnixTime(&lt);
  296. }
  297. *file_size=FileSize64(hFile);
  298. CloseHandle(hFile);
  299. }
  300. }
  301. else
  302. {
  303. // success
  304. *file_time = FileTimeToUnixTime(&file_data.ftLastWriteTime);
  305. LARGE_INTEGER size64;
  306. size64.LowPart = file_data.nFileSizeLow;
  307. size64.HighPart = file_data.nFileSizeHigh;
  308. *file_size = size64.QuadPart;
  309. }
  310. }
  311. void getTitle(Device * dev, songid_t song, const wchar_t * filename,wchar_t * buf, int len) {
  312. buf[0]=0; buf[len-1]=0;
  313. tagItem item = {song,dev,filename};
  314. waFormatTitleExtended fmt={filename,0,NULL,&item,buf,len,tagFunc,tagFreeFunc};
  315. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED);
  316. }
  317. #define atoi_NULLOK(s) ((s)?_wtoi(s):0)
  318. void filenameToItemRecord(wchar_t * file, itemRecordW * ice)
  319. {
  320. int gtrack=0;
  321. wchar_t *gartist=NULL,*galbum=NULL,*gtitle=NULL;
  322. wchar_t *guessbuf = guessTitles(file,&gtrack,&gartist,&galbum,&gtitle);
  323. if(!gartist) gartist=L"";
  324. if(!galbum) galbum=L"";
  325. if(!gtitle) gtitle=L"";
  326. wchar_t buf[512]=L"";
  327. extendedFileInfoStructW efs={file,NULL,buf,512};
  328. efs.metadata=L"title"; buf[0]=0;
  329. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  330. if(buf[0]) { ice->title=_wcsdup(buf); gartist=L""; galbum=L""; gtrack=-1;}
  331. else ice->title=_wcsdup(gtitle);
  332. efs.metadata=L"album";
  333. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  334. if(buf[0]) ice->album=_wcsdup(buf);
  335. else ice->album=_wcsdup(galbum);
  336. efs.metadata=L"artist"; buf[0]=0;
  337. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  338. if(buf[0]) ice->artist=_wcsdup(buf);
  339. else ice->artist=_wcsdup(gartist);
  340. efs.metadata=L"comment"; buf[0]=0;
  341. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  342. ice->comment=_wcsdup(buf);
  343. efs.metadata=L"genre"; buf[0]=0;
  344. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  345. ice->genre=_wcsdup(buf);
  346. efs.metadata=L"albumartist"; buf[0]=0;
  347. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  348. ice->albumartist=_wcsdup(buf);
  349. efs.metadata=L"replaygain_album_gain"; buf[0]=0;
  350. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  351. ice->replaygain_album_gain=_wcsdup(buf);
  352. efs.metadata=L"replaygain_track_gain"; buf[0]=0;
  353. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  354. ice->replaygain_track_gain=_wcsdup(buf);
  355. efs.metadata=L"publisher"; buf[0]=0;
  356. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  357. ice->publisher=_wcsdup(buf);
  358. efs.metadata=L"composer"; buf[0]=0;
  359. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  360. ice->composer=_wcsdup(buf);
  361. efs.metadata=L"year"; buf[0]=0;
  362. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  363. ice->year=atoi_NULLOK(buf);
  364. efs.metadata=L"track"; buf[0]=0;
  365. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  366. if(buf[0]) ice->track=atoi_NULLOK(buf);
  367. else ice->track=gtrack;
  368. efs.metadata=L"tracks"; buf[0]=0;
  369. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  370. ice->tracks=atoi_NULLOK(buf);
  371. efs.metadata=L"rating"; buf[0]=0;
  372. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  373. ice->rating=atoi_NULLOK(buf);
  374. __int64 file_size=INVALID_FILE_SIZE;
  375. time_t file_time=0;
  376. GetFileSizeAndTime(file, &file_size, &file_time);
  377. if (!(file_size == INVALID_FILE_SIZE || file_size == 0))
  378. {
  379. ice->filetime=file_time;
  380. // scales to the kb value this uses
  381. ice->filesize=(int)(file_size/1024);
  382. // and since 5.64+ we can also return this as a true value
  383. StringCchPrintf(buf, sizeof(buf), L"%d", file_size);
  384. setRecordExtendedItem(ice,L"realsize",buf);
  385. }
  386. ice->lastupd=time(NULL);
  387. efs.metadata=L"bitrate"; buf[0]=0;
  388. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  389. ice->bitrate=atoi_NULLOK(buf);
  390. efs.metadata=L"type"; buf[0]=0;
  391. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  392. ice->type=atoi_NULLOK(buf);
  393. efs.metadata=L"disc"; buf[0]=0;
  394. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  395. ice->disc=atoi_NULLOK(buf);
  396. efs.metadata=L"discs"; buf[0]=0;
  397. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  398. ice->discs=atoi_NULLOK(buf);
  399. efs.metadata=L"bpm"; buf[0]=0;
  400. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  401. ice->bpm=atoi_NULLOK(buf);
  402. basicFileInfoStructW b={efs.filename,0};
  403. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&b,IPC_GET_BASIC_FILE_INFOW);
  404. ice->length=b.length;
  405. // additional fields to match (if available) with a full library
  406. // response this is mainly for improving the cloud compatibility
  407. efs.metadata=L"lossless"; buf[0]=0;
  408. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  409. if(buf[0]) setRecordExtendedItem(ice,L"lossless",buf);
  410. efs.metadata=L"director"; buf[0]=0;
  411. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  412. if(buf[0]) setRecordExtendedItem(ice,L"director",buf);
  413. efs.metadata=L"producer"; buf[0]=0;
  414. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  415. if(buf[0]) setRecordExtendedItem(ice,L"producer",buf);
  416. efs.metadata=L"width"; buf[0]=0;
  417. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  418. if(buf[0]) setRecordExtendedItem(ice,L"width",buf);
  419. efs.metadata=L"height"; buf[0]=0;
  420. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  421. if(buf[0]) setRecordExtendedItem(ice,L"height",buf);
  422. efs.metadata=L"mime"; buf[0]=0;
  423. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efs,IPC_GET_EXTENDED_FILE_INFOW);
  424. if(buf[0]) setRecordExtendedItem(ice,L"mime",buf);
  425. // Not filled in are: playcount, lastplay
  426. ice->filename = _wcsdup(file);
  427. free(guessbuf);
  428. }
  429. void copyTags(itemRecordW * in, wchar_t * out) {
  430. // check if the old file still exists - if it does, we will let Winamp copy metadata for us
  431. if (wcscmp(in->filename, out) && PathFileExists(in->filename))
  432. {
  433. copyFileInfoStructW copy;
  434. copy.dest = out;
  435. copy.source = in->filename;
  436. if(SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&copy, IPC_COPY_EXTENDED_FILE_INFOW) == 0) // 0 means success
  437. return;
  438. }
  439. }