1
0

AlbumArt.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095
  1. #include "AlbumArt.h"
  2. #include "api__albumart.h"
  3. #include "ParamList.h"
  4. #include <api/service/svcs/svc_imgload.h>
  5. #include <api/service/svcs/svc_imgwrite.h>
  6. #include <shlwapi.h>
  7. #include <api/service/waservicefactory.h>
  8. #include "../Agave/AlbumArt/svc_albumArtProvider.h"
  9. #include <api/syscb/callbacks/metacb.h>
  10. #include <strsafe.h>
  11. static svc_imageLoader *FindImageLoader(const wchar_t *filespec, waServiceFactory **factory)
  12. {
  13. FOURCC imgload = svc_imageLoader::getServiceType();
  14. int n = (int)WASABI_API_SVC->service_getNumServices(imgload);
  15. for (int i=0; i<n; i++)
  16. {
  17. waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
  18. if (sf)
  19. {
  20. svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
  21. if (l)
  22. {
  23. if (l->isMine(filespec))
  24. {
  25. *factory = sf;
  26. return l;
  27. }
  28. sf->releaseInterface(l);
  29. }
  30. }
  31. }
  32. return NULL;
  33. }
  34. static svc_imageLoader *FindImageLoader(void *data, size_t datalen, waServiceFactory **factory)
  35. {
  36. FOURCC imgload = svc_imageLoader::getServiceType();
  37. int n = (int)WASABI_API_SVC->service_getNumServices(imgload);
  38. for (int i=0; i<n; i++)
  39. {
  40. waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
  41. if (sf)
  42. {
  43. svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
  44. if (l)
  45. {
  46. if (l->testData(data, (int)datalen))
  47. {
  48. *factory = sf;
  49. return l;
  50. }
  51. sf->releaseInterface(l);
  52. }
  53. }
  54. }
  55. return NULL;
  56. }
  57. static svc_albumArtProvider *FindProvider(const wchar_t *filename, int providerType, waServiceFactory **factory)
  58. {
  59. FOURCC albumartprovider = svc_albumArtProvider::getServiceType();
  60. int n = (int)WASABI_API_SVC->service_getNumServices(albumartprovider);
  61. for (int i=0; i<n; i++)
  62. {
  63. waServiceFactory *sf = WASABI_API_SVC->service_enumService(albumartprovider,i);
  64. if (sf)
  65. {
  66. svc_albumArtProvider * provider = (svc_albumArtProvider*)sf->getInterface();
  67. if (provider)
  68. {
  69. if (provider->ProviderType() == providerType && provider->IsMine(filename))
  70. {
  71. *factory = sf;
  72. return provider;
  73. }
  74. sf->releaseInterface(provider);
  75. }
  76. }
  77. }
  78. return NULL;
  79. }
  80. static ARGB32 *loadImgFromFile(const wchar_t *file, int *w, int *h)
  81. {
  82. waServiceFactory *sf = 0;
  83. svc_imageLoader *loader = FindImageLoader(file, &sf);
  84. if (loader)
  85. {
  86. HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  87. if (hf != INVALID_HANDLE_VALUE)
  88. {
  89. int len = GetFileSize(hf, 0);
  90. HANDLE hmap = CreateFileMapping(hf, 0, PAGE_READONLY, 0, 0, 0);
  91. if (hmap)
  92. {
  93. void *data = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0);
  94. if (data)
  95. {
  96. if (loader->testData(data,len))
  97. {
  98. ARGB32* im = loader->loadImage(data,len,w,h);
  99. UnmapViewOfFile(data);
  100. CloseHandle(hmap);
  101. CloseHandle(hf);
  102. sf->releaseInterface(loader);
  103. return im;
  104. }
  105. UnmapViewOfFile(data);
  106. }
  107. CloseHandle(hmap);
  108. }
  109. CloseHandle(hf);
  110. }
  111. sf->releaseInterface(loader);
  112. }
  113. return 0;
  114. }
  115. static ARGB32 *loadImgFromFile(const wchar_t *path, const wchar_t *filespec, int *w, int *h, bool test=false, ifc_xmlreaderparams *params = NULL)
  116. {
  117. waServiceFactory *sf = 0;
  118. svc_imageLoader *loader = FindImageLoader(filespec, &sf);
  119. if (loader)
  120. {
  121. if (test)
  122. {
  123. sf->releaseInterface(loader);
  124. return (ARGB32*)1;
  125. }
  126. wchar_t file[MAX_PATH] = {0};
  127. PathCombineW(file, path, filespec);
  128. HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  129. if (hf != INVALID_HANDLE_VALUE)
  130. {
  131. int len = GetFileSize(hf, 0);
  132. HANDLE hmap = CreateFileMapping(hf, 0, PAGE_READONLY, 0, 0, 0);
  133. if (hmap)
  134. {
  135. void *data = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0);
  136. if (data)
  137. {
  138. if (loader->testData(data,len))
  139. {
  140. ARGB32* im = loader->loadImage(data,len,w,h, params);
  141. UnmapViewOfFile(data);
  142. CloseHandle(hmap);
  143. CloseHandle(hf);
  144. sf->releaseInterface(loader);
  145. return im;
  146. }
  147. UnmapViewOfFile(data);
  148. }
  149. CloseHandle(hmap);
  150. }
  151. CloseHandle(hf);
  152. }
  153. sf->releaseInterface(loader);
  154. }
  155. return 0;
  156. }
  157. static bool loadImgDataFromFile(const wchar_t *path, const wchar_t *filespec, void **bits, size_t *len, wchar_t **mimeType, bool originTest = false)
  158. {
  159. waServiceFactory *sf = 0;
  160. svc_imageLoader *loader = FindImageLoader(filespec, &sf);
  161. if (loader)
  162. {
  163. wchar_t file[MAX_PATH] = {0};
  164. PathCombineW(file, path, filespec);
  165. HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  166. if (hf != INVALID_HANDLE_VALUE)
  167. {
  168. if(!originTest)
  169. {
  170. int flen = GetFileSize(hf, 0);
  171. *bits = WASABI_API_MEMMGR->sysMalloc(flen);
  172. DWORD bytes_read = 0;
  173. ReadFile(hf, *bits, flen, &bytes_read, 0);
  174. *len = bytes_read;
  175. }
  176. if (mimeType)
  177. {
  178. *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(12 * sizeof(wchar_t));
  179. wcsncpy(*mimeType, loader->mimeType(), 12);
  180. }
  181. CloseHandle(hf);
  182. sf->releaseInterface(loader);
  183. return true;
  184. }
  185. sf->releaseInterface(loader);
  186. }
  187. return false;
  188. }
  189. static ARGB32 *FindImage(const wchar_t *path, const wchar_t *mask, int *w, int *h, bool test=false, ifc_xmlreaderparams *params = NULL)
  190. {
  191. wchar_t dirmask[MAX_PATH] = {0};
  192. PathCombineW(dirmask, path, mask);
  193. WIN32_FIND_DATAW find = {0};
  194. HANDLE hFind = FindFirstFileW(dirmask, &find);
  195. if (hFind != INVALID_HANDLE_VALUE)
  196. {
  197. do
  198. {
  199. ARGB32 *bits = loadImgFromFile(path, find.cFileName, w, h, test, params);
  200. if (bits)
  201. {
  202. FindClose(hFind);
  203. return bits;
  204. }
  205. }
  206. while (FindNextFileW(hFind, &find));
  207. FindClose(hFind);
  208. }
  209. return 0;
  210. }
  211. static bool FindImageData(const wchar_t *path, const wchar_t *mask, void **bits, size_t *len, wchar_t **mimeType)
  212. {
  213. wchar_t dirmask[MAX_PATH] = {0};
  214. PathCombineW(dirmask, path, mask);
  215. WIN32_FIND_DATAW find = {0};
  216. HANDLE hFind = FindFirstFileW(dirmask, &find);
  217. if (hFind != INVALID_HANDLE_VALUE)
  218. {
  219. do
  220. {
  221. if (loadImgDataFromFile(path, find.cFileName, bits, len, mimeType))
  222. {
  223. return true;
  224. }
  225. }
  226. while (FindNextFileW(hFind, &find));
  227. FindClose(hFind);
  228. }
  229. return false;
  230. }
  231. static bool FindImageOrigin(const wchar_t *path, const wchar_t *mask, wchar_t **mimeType)
  232. {
  233. wchar_t dirmask[MAX_PATH] = {0};
  234. PathCombineW(dirmask, path, mask);
  235. WIN32_FIND_DATAW find = {0};
  236. HANDLE hFind = FindFirstFileW(dirmask, &find);
  237. if (hFind != INVALID_HANDLE_VALUE)
  238. {
  239. do
  240. {
  241. if (loadImgDataFromFile(path, find.cFileName, NULL, NULL, mimeType, true))
  242. {
  243. FindClose(hFind);
  244. return true;
  245. }
  246. }
  247. while (FindNextFileW(hFind, &find));
  248. FindClose(hFind);
  249. }
  250. return false;
  251. }
  252. static bool DeleteImage(const wchar_t *path, const wchar_t *mask)
  253. {
  254. wchar_t dirmask[MAX_PATH] = {0};
  255. PathCombineW(dirmask, path, mask);
  256. WIN32_FIND_DATAW find = {0};
  257. HANDLE hFind = FindFirstFileW(dirmask, &find);
  258. if (hFind != INVALID_HANDLE_VALUE)
  259. {
  260. do
  261. {
  262. // i know this seems stupid, but we need to load the image
  263. // since this is supposed to delete the image that would show up
  264. // from a GetAlbumArt call
  265. int w = 0, h = 0;
  266. ARGB32 *bits = loadImgFromFile(path, find.cFileName, &w, &h);
  267. if (bits)
  268. {
  269. FindClose(hFind);
  270. WASABI_API_MEMMGR->sysFree(bits);
  271. wchar_t fullpath[MAX_PATH] = {0};
  272. PathCombineW(fullpath, path, find.cFileName);
  273. DeleteFileW(fullpath);
  274. return true;
  275. }
  276. }
  277. while (FindNextFileW(hFind, &find));
  278. FindClose(hFind);
  279. }
  280. return false;
  281. }
  282. static ARGB32 *FindAlbumArtByProvider(int providertype, const wchar_t *filename, const wchar_t *type, int *w, int *h, ifc_xmlreaderparams *params = NULL)
  283. {
  284. waServiceFactory *factory = 0;
  285. svc_albumArtProvider *provider = FindProvider(filename, providertype, &factory);
  286. if (provider)
  287. {
  288. void *data = 0;
  289. size_t datalen = 0;
  290. wchar_t *mimeType=0;
  291. if (provider->GetAlbumArtData(filename, type, &data, &datalen, &mimeType) == ALBUMARTPROVIDER_SUCCESS && data && datalen)
  292. {
  293. waServiceFactory *sf;
  294. svc_imageLoader *loader = 0;
  295. if (mimeType)
  296. {
  297. wchar_t mask[MAX_PATH] = {0};
  298. StringCchPrintfW(mask, MAX_PATH, L"hi.%s", mimeType);
  299. WASABI_API_MEMMGR->sysFree(mimeType);
  300. loader = FindImageLoader(mask, &sf);
  301. }
  302. else
  303. {
  304. loader = FindImageLoader(data, datalen, &sf);
  305. }
  306. if (loader)
  307. {
  308. if (loader->testData(data, (int)datalen))
  309. {
  310. ARGB32* im = loader->loadImage(data, (int)datalen,w,h,params);
  311. WASABI_API_MEMMGR->sysFree(data);
  312. sf->releaseInterface(loader);
  313. factory->releaseInterface(provider);
  314. return im;
  315. }
  316. sf->releaseInterface(loader);
  317. }
  318. WASABI_API_MEMMGR->sysFree(data);
  319. }
  320. factory->releaseInterface(provider);
  321. }
  322. return 0;
  323. }
  324. static int DeleteAlbumArtByProvider(int providertype, const wchar_t *filename, const wchar_t *type)
  325. {
  326. waServiceFactory *factory = 0;
  327. svc_albumArtProvider *provider = FindProvider(filename, providertype, &factory);
  328. if (provider)
  329. {
  330. int ret = provider->DeleteAlbumArt(filename, type);
  331. factory->releaseInterface(provider);
  332. return ret == ALBUMARTPROVIDER_SUCCESS;
  333. }
  334. return false;
  335. }
  336. static bool FindSceneNFO(const wchar_t *path, wchar_t *mask)
  337. {
  338. wchar_t nfo_mask[MAX_PATH] = {0};
  339. PathCombineW(nfo_mask, path, L"*.nfo");
  340. WIN32_FIND_DATAW find = {0};
  341. HANDLE hFind = FindFirstFileW(nfo_mask, &find);
  342. if (hFind != INVALID_HANDLE_VALUE)
  343. {
  344. StringCchCopyW(mask, MAX_PATH, find.cFileName);
  345. PathRemoveExtensionW(mask);
  346. StringCchCatW(mask, MAX_PATH, L".*");
  347. FindClose(hFind);
  348. return true;
  349. }
  350. return false;
  351. }
  352. static void CleanNameForPath(wchar_t *name)
  353. {
  354. while (name && *name)
  355. {
  356. switch(*name)
  357. {
  358. case L'?':
  359. case L'*':
  360. case L'|':
  361. *name = L'_';
  362. break;
  363. case '/':
  364. case L'\\':
  365. case L':':
  366. *name = L'-';
  367. break;
  368. case L'\"':
  369. *name = L'\'';
  370. break;
  371. case L'<':
  372. *name = L'(';
  373. break;
  374. case L'>': *name = L')';
  375. break;
  376. }
  377. name++;
  378. }
  379. }
  380. int AlbumArt::GetAlbumArt(const wchar_t *filename, const wchar_t *type, int *w, int *h, ARGB32 **bits)
  381. {
  382. if (!filename || !*filename)
  383. return ALBUMART_FAILURE;
  384. /* First, look for embedded album art */
  385. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_EMBEDDED, filename, type, w,h))
  386. return ALBUMART_SUCCESS;
  387. /* moved to allow for SHOUTcast 2 in-stream metadata which is best classed as embedded */
  388. if (PathIsURLW(filename))
  389. return ALBUMART_FAILURE;
  390. /* Next, Search the albumart in a cover library dir */
  391. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_DATABASE, filename, type, w,h))
  392. return ALBUMART_SUCCESS;
  393. bool isCover = !_wcsicmp(type,L"cover");
  394. /* Get the folder of the file */
  395. wchar_t path[MAX_PATH] = {0};
  396. wchar_t mask[MAX_PATH] = {0};
  397. StringCchCopyW(path, MAX_PATH, filename);
  398. PathRemoveFileSpecW(path);
  399. /* Next, look for a file with the same name as the album name */
  400. wchar_t albumname[MAX_PATH] = {0};
  401. if (isCover && AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  402. {
  403. CleanNameForPath(albumname);
  404. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  405. if (*bits = FindImage(path, mask, w, h))
  406. return ALBUMART_SUCCESS;
  407. }
  408. // look for 'scene' artwork (*.jpg with the same filename as *.nfo)
  409. if (isCover && FindSceneNFO(path, mask))
  410. {
  411. if (*bits = FindImage(path, mask, w, h))
  412. return ALBUMART_SUCCESS;
  413. }
  414. /* Next, let's look in the folder for some art */
  415. StringCchPrintfW(mask, MAX_PATH, L"%s.*", type);
  416. if (*bits = FindImage(path, mask, w, h))
  417. return ALBUMART_SUCCESS;
  418. /* Look for folder.jpg if the type is "cover" */
  419. if (isCover && (*bits = FindImage(path, L"folder.*", w, h)))
  420. return ALBUMART_SUCCESS;
  421. /* Look for front.jpg if the type is "cover" */
  422. if (isCover && (*bits = FindImage(path, L"front.*", w, h)))
  423. return ALBUMART_SUCCESS;
  424. /* Look for albumart.jpg if the type is "cover" */
  425. if (isCover && (*bits = FindImage(path, L"albumart.*", w, h)))
  426. return ALBUMART_SUCCESS;
  427. return ALBUMART_FAILURE;
  428. }
  429. int AlbumArt::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
  430. {
  431. if (!filename || !*filename)
  432. return ALBUMART_FAILURE;
  433. if (PathIsURLW(filename))
  434. return ALBUMART_FAILURE;
  435. /* First, look for embedded album art */
  436. waServiceFactory *sourceFactory = 0;
  437. svc_albumArtProvider *sourceProvider = FindProvider(filename, ALBUMARTPROVIDER_TYPE_EMBEDDED, &sourceFactory);
  438. if (sourceProvider)
  439. {
  440. void *data = 0;
  441. size_t datalen = 0;
  442. wchar_t *mime_type = 0;
  443. if (sourceProvider->GetAlbumArtData(filename, type, &data, &datalen, &mime_type) == ALBUMARTPROVIDER_SUCCESS && data && datalen)
  444. {
  445. if (bits)
  446. *bits = data;
  447. if (len)
  448. *len = datalen;
  449. if (mimeType)
  450. *mimeType = mime_type;
  451. sourceFactory->releaseInterface(sourceProvider);
  452. return ALBUMART_SUCCESS;
  453. }
  454. sourceFactory->releaseInterface(sourceProvider);
  455. }
  456. #if 0 // TODO
  457. /* Next, Search the albumart in a cover library dir */
  458. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_DATABASE, filename, type, w,h))
  459. return ALBUMART_SUCCESS;
  460. #endif
  461. bool isCover = !_wcsicmp(type,L"cover");
  462. /* Get the folder of the file */
  463. wchar_t path[MAX_PATH] = {0};
  464. wchar_t mask[MAX_PATH] = {0};
  465. StringCchCopyW(path, MAX_PATH, filename);
  466. PathRemoveFileSpecW(path);
  467. /* Next, look for a file with the same name as the album name */
  468. wchar_t albumname[MAX_PATH] = {0};
  469. if (isCover && AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  470. {
  471. CleanNameForPath(albumname);
  472. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  473. if (FindImageData(path, mask, bits, len, mimeType))
  474. return ALBUMART_SUCCESS;
  475. }
  476. // look for 'scene' artwork (*.jpg with the same filename as *.nfo)
  477. if (isCover && FindSceneNFO(path, mask))
  478. {
  479. if (FindImageData(path, mask, bits, len, mimeType))
  480. return ALBUMART_SUCCESS;
  481. }
  482. /* Next, let's look in the folder for some art */
  483. StringCchPrintfW(mask, MAX_PATH, L"%s.*", type);
  484. if (FindImageData(path, mask, bits, len, mimeType))
  485. return ALBUMART_SUCCESS;
  486. /* Look for folder.jpg if the type is "cover" */
  487. if (isCover && FindImageData(path, L"folder.*", bits, len, mimeType))
  488. return ALBUMART_SUCCESS;
  489. /* Look for front.jpg if the type is "cover" */
  490. if (isCover && FindImageData(path, L"front.*", bits, len, mimeType))
  491. return ALBUMART_SUCCESS;
  492. /* Look for albumart.jpg if the type is "cover" */
  493. if (isCover && FindImageData(path, L"albumart.*", bits, len, mimeType))
  494. return ALBUMART_SUCCESS;
  495. return ALBUMART_FAILURE;
  496. }
  497. int AlbumArt::GetAlbumArt_NoAMG(const wchar_t *filename, const wchar_t *type, int *w, int *h, ARGB32 **bits)
  498. {
  499. ParamList params;
  500. params.addItem(L"AMG", L"1");
  501. if (!filename || !*filename)
  502. return ALBUMART_FAILURE;
  503. if (PathIsURLW(filename))
  504. return ALBUMART_FAILURE;
  505. /* First, look for embedded album art */
  506. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_EMBEDDED, filename, type, w,h, &params))
  507. return ALBUMART_SUCCESS;
  508. /* Next, Search the albumart in a cover library dir */
  509. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_DATABASE, filename, type, w,h, &params))
  510. return ALBUMART_SUCCESS;
  511. bool isCover = !_wcsicmp(type,L"cover");
  512. /* Get the folder of the file */
  513. wchar_t path[MAX_PATH] = {0};
  514. wchar_t mask[MAX_PATH] = {0};
  515. StringCchCopyW(path, MAX_PATH, filename);
  516. PathRemoveFileSpecW(path);
  517. /* Next, look for a file with the same name as the album name */
  518. wchar_t albumname[MAX_PATH] = {0};
  519. if (isCover && AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  520. {
  521. CleanNameForPath(albumname);
  522. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  523. if (*bits = FindImage(path, mask, w, h, false, &params))
  524. return ALBUMART_SUCCESS;
  525. }
  526. // look for 'scene' artwork (*.jpg with the same filename as *.nfo)
  527. if (isCover && FindSceneNFO(path, mask))
  528. {
  529. if (*bits = FindImage(path, mask, w, h, false, &params))
  530. return ALBUMART_SUCCESS;
  531. }
  532. /* Next, let's look in the folder for some art */
  533. StringCchPrintfW(mask, MAX_PATH, L"%s.*", type);
  534. if (*bits = FindImage(path, mask, w, h, false, &params))
  535. return ALBUMART_SUCCESS;
  536. /* Look for folder.jpg if the type is "cover" */
  537. if (isCover && (*bits = FindImage(path, L"folder.*", w, h, false, &params)))
  538. return ALBUMART_SUCCESS;
  539. /* Look for front.jpg if the type is "cover" */
  540. if (isCover && (*bits = FindImage(path, L"front.*", w, h, false, &params)))
  541. return ALBUMART_SUCCESS;
  542. /* Look for albumart.jpg if the type is "cover" */
  543. if (isCover && (*bits = FindImage(path, L"albumart.*", w, h, false, &params)))
  544. return ALBUMART_SUCCESS;
  545. return ALBUMART_FAILURE;
  546. }
  547. int AlbumArt::GetAlbumArtOrigin(const wchar_t *filename, const wchar_t *type, wchar_t **mimeType)
  548. {
  549. if (!filename || !*filename)
  550. return ALBUMART_NONE;
  551. if (PathIsURLW(filename))
  552. return ALBUMART_NONE;
  553. /* First, look for embedded album art */
  554. waServiceFactory *sourceFactory = 0;
  555. svc_albumArtProvider *sourceProvider = FindProvider(filename, ALBUMARTPROVIDER_TYPE_EMBEDDED, &sourceFactory);
  556. if (sourceProvider)
  557. {
  558. void *data = 0;
  559. size_t datalen = 0;
  560. wchar_t *mime_type = 0;
  561. if (sourceProvider->GetAlbumArtData(filename, type, &data, &datalen, &mime_type) == ALBUMARTPROVIDER_SUCCESS && data && datalen)
  562. {
  563. if (mimeType)
  564. {
  565. *mimeType = mime_type;
  566. if(!mime_type)
  567. {
  568. waServiceFactory *sf = 0;
  569. svc_imageLoader *loader = 0;
  570. loader = FindImageLoader(data, datalen, &sf);
  571. if (loader)
  572. {
  573. *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(16 * sizeof(wchar_t));
  574. wcsncpy(*mimeType, loader->mimeType(), 11);
  575. sf->releaseInterface(loader);
  576. }
  577. else
  578. {
  579. *mimeType = 0;
  580. }
  581. }
  582. }
  583. sourceFactory->releaseInterface(sourceProvider);
  584. WASABI_API_MEMMGR->sysFree(data);
  585. return ALBUMART_EMBEDDED;
  586. }
  587. sourceFactory->releaseInterface(sourceProvider);
  588. }
  589. #if 0 // TODO
  590. /* Next, Search the albumart in a cover library dir */
  591. if (*bits = FindAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_DATABASE, filename, type, w,h))
  592. return ALBUMART_SUCCESS;
  593. #endif
  594. bool isCover = !_wcsicmp(type,L"cover");
  595. /* Get the folder of the file */
  596. wchar_t path[MAX_PATH] = {0};
  597. wchar_t mask[MAX_PATH] = {0};
  598. StringCchCopyW(path, MAX_PATH, filename);
  599. PathRemoveFileSpecW(path);
  600. /* Next, look for a file with the same name as the album name */
  601. wchar_t albumname[MAX_PATH] = {0};
  602. if (isCover && AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  603. {
  604. CleanNameForPath(albumname);
  605. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  606. if (FindImageOrigin(path, mask, mimeType))
  607. return ALBUMART_ALBUM;
  608. }
  609. // look for 'scene' artwork (*.jpg with the same filename as *.nfo)
  610. if (isCover && FindSceneNFO(path, mask))
  611. {
  612. if (FindImageOrigin(path, mask, mimeType))
  613. return ALBUMART_NFO;
  614. }
  615. /* Next, let's look in the folder for some art */
  616. StringCchPrintfW(mask, MAX_PATH, L"%s.*", type);
  617. if (FindImageOrigin(path, mask, mimeType))
  618. return ALBUMART_FILENAME;
  619. /* Look for folder.jpg if the type is "cover" */
  620. if (isCover && FindImageOrigin(path, L"folder.*", mimeType))
  621. return ALBUMART_FOLDER;
  622. /* Look for front.jpg if the type is "cover" */
  623. if (isCover && FindImageOrigin(path, L"front.*", mimeType))
  624. return ALBUMART_FRONT;
  625. /* Look for albumart.jpg if the type is "cover" */
  626. if (isCover && FindImageOrigin(path, L"albumart.*", mimeType))
  627. return ALBUMART_ARTWORK;
  628. return ALBUMART_NONE;
  629. }
  630. // benski> TODO, i really don't like duplicating this logic from GetAlbumArt, maybe we can find a way
  631. int AlbumArt::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
  632. {
  633. if (!filename || !*filename)
  634. return ALBUMART_FAILURE;
  635. if (PathIsURLW(filename))
  636. return ALBUMART_FAILURE;
  637. /* First, look for embedded album art */
  638. if (DeleteAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_EMBEDDED, filename, type))
  639. return ALBUMART_SUCCESS;
  640. /* Next, Search the albumart in a cover library dir */
  641. if (DeleteAlbumArtByProvider(ALBUMARTPROVIDER_TYPE_DATABASE, filename, type))
  642. return ALBUMART_SUCCESS;
  643. bool isCover = !_wcsicmp(type,L"cover");
  644. /* Get the folder of the file */
  645. wchar_t path[MAX_PATH] = {0};
  646. wchar_t mask[MAX_PATH] = {0};
  647. StringCchCopyW(path, MAX_PATH, filename);
  648. PathRemoveFileSpecW(path);
  649. /* Next, look for a file with the same name as the album name */
  650. wchar_t albumname[MAX_PATH] = {0};
  651. if (AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  652. {
  653. CleanNameForPath(albumname);
  654. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  655. if (DeleteImage(path, mask))
  656. return ALBUMART_SUCCESS;
  657. }
  658. // look for 'scene' artwork (*.jpg with the same filename as *.nfo)
  659. if (isCover && FindSceneNFO(path, mask))
  660. {
  661. if (DeleteImage(path, mask))
  662. return ALBUMART_SUCCESS;
  663. }
  664. /* Next, let's look in the folder for some art */
  665. StringCchPrintfW(mask, MAX_PATH, L"%s.*", type);
  666. if (DeleteImage(path, mask))
  667. return ALBUMART_SUCCESS;
  668. /* Look for folder.jpg if the type is "cover" */
  669. if (isCover && DeleteImage(path, L"folder.*"))
  670. return ALBUMART_SUCCESS;
  671. /* Look for folder.jpg if the type is "cover" */
  672. if (isCover && DeleteImage(path, L"front.*"))
  673. return ALBUMART_SUCCESS;
  674. /* Look for albumart.jpg if the type is "cover" */
  675. if (isCover && DeleteImage(path, L"albumart.*"))
  676. return ALBUMART_SUCCESS;
  677. return ALBUMART_FAILURE;
  678. }
  679. class strbuilder
  680. {
  681. public:
  682. wchar_t *str;
  683. wchar_t *end;
  684. size_t alloc;
  685. strbuilder()
  686. {
  687. alloc = 512;
  688. end = str = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(alloc);
  689. str[0]=str[1]=0;
  690. }
  691. void append(const wchar_t *s)
  692. {
  693. size_t oldlen = end - str;
  694. size_t l = wcslen(s) + 1;
  695. while (alloc < l + oldlen + 1)
  696. {
  697. alloc += 512;
  698. str = (wchar_t*)WASABI_API_MEMMGR->sysRealloc(str,alloc);
  699. end = str+oldlen;
  700. }
  701. lstrcpynW(end,s, (int)l);
  702. end += l;
  703. *end=0;
  704. }
  705. wchar_t *get()
  706. {
  707. return str;
  708. }
  709. };
  710. int AlbumArt::GetAlbumArtTypes(const wchar_t *filename, wchar_t **types)
  711. {
  712. if (!filename || !*filename)
  713. return ALBUMART_FAILURE;
  714. if (PathIsURLW(filename))
  715. return ALBUMART_FAILURE;
  716. wchar_t path[MAX_PATH] = {0};
  717. wchar_t mask[MAX_PATH] = {0};
  718. strbuilder str;
  719. /* First, let's look in the folder for some art */
  720. StringCchCopyW(path, MAX_PATH, filename);
  721. PathRemoveFileSpecW(path);
  722. // TODO (mpdeimos) Make the stuff here configurable, add wildcards *front* / *cover* ...
  723. StringCchPrintfW(mask, MAX_PATH, L"cover.*");
  724. if (FindImage(path, mask, 0, 0,true))
  725. str.append(L"cover");
  726. else // mpdeimos> front.jpg is much more common than cover.jpg
  727. {
  728. StringCchPrintfW(mask, MAX_PATH, L"front.*");
  729. if (FindImage(path, mask, 0, 0,true))
  730. str.append(L"cover");
  731. else
  732. {
  733. StringCchPrintfW(mask, MAX_PATH, L"folder.*");
  734. if (FindImage(path, mask, 0, 0,true))
  735. str.append(L"cover");
  736. else
  737. {
  738. StringCchPrintfW(mask, MAX_PATH, L"albumart.*");
  739. if (FindImage(path, mask, 0, 0,true))
  740. str.append(L"cover");
  741. else
  742. {
  743. wchar_t albumname[MAX_PATH]=L"";
  744. if (AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  745. {
  746. CleanNameForPath(albumname);
  747. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  748. if (FindImage(path, mask, 0, 0))
  749. str.append(L"cover");
  750. }
  751. }
  752. }
  753. }
  754. }
  755. // add other shit to str
  756. //str.append(L"foo");
  757. //str.append(L"bar");
  758. *types = str.get();
  759. return ALBUMART_SUCCESS;
  760. }
  761. int AlbumArt::GetValidAlbumArtTypes(const wchar_t *filename, wchar_t **type)
  762. {
  763. if (!filename || !*filename)
  764. return ALBUMART_FAILURE;
  765. if (PathIsURLW(filename))
  766. return ALBUMART_FAILURE;
  767. strbuilder str;
  768. str.append(L"cover");
  769. // add other shit to str
  770. //str.append(L"foo");
  771. //str.append(L"bar");
  772. *type = str.get();
  773. return ALBUMART_SUCCESS;
  774. }
  775. static void * writeImg(const ARGB32 *data, int w, int h, int *length, const wchar_t *ext)
  776. {
  777. if (!ext || (ext && !*ext)) return NULL;
  778. if (*ext == L'.') ext++;
  779. FOURCC imgwrite = svc_imageWriter::getServiceType();
  780. int n = (int)WASABI_API_SVC->service_getNumServices(imgwrite);
  781. for (int i=0; i<n; i++)
  782. {
  783. waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgwrite,i);
  784. if (sf)
  785. {
  786. svc_imageWriter * l = (svc_imageWriter*)sf->getInterface();
  787. if (l)
  788. {
  789. if (wcsstr(l->getExtensions(),ext))
  790. {
  791. void* ret = l->convert(data,32,w,h,length);
  792. sf->releaseInterface(l);
  793. return ret;
  794. }
  795. sf->releaseInterface(l);
  796. }
  797. }
  798. }
  799. return NULL;
  800. }
  801. static int writeFile(const wchar_t *file, const void * data, int length)
  802. {
  803. FILE *f=_wfopen(file,L"wb");
  804. if (!f) return ALBUMART_FAILURE;
  805. if (fwrite(data,length,1,f) != 1)
  806. {
  807. fclose(f);
  808. return ALBUMART_FAILURE;
  809. }
  810. fclose(f);
  811. return ALBUMART_SUCCESS;
  812. }
  813. static void writeImageToFile(ARGB32 * img, int w, int h, const wchar_t *file)
  814. {
  815. int length = 0;
  816. void * data = writeImg(img,w,h,&length,wcsrchr(file,L'.'));
  817. if (data)
  818. {
  819. writeFile(file,data,length);
  820. WASABI_API_MEMMGR->sysFree(data);
  821. }
  822. }
  823. int AlbumArt::SetAlbumArt(const wchar_t *filename, const wchar_t *type, int w, int h, const void *bits, size_t len, const wchar_t *mimeType)
  824. {
  825. if (!bits)
  826. return ALBUMART_FAILURE;
  827. if (!filename || !*filename)
  828. return ALBUMART_FAILURE;
  829. if (PathIsURLW(filename))
  830. return ALBUMART_FAILURE;
  831. if (!type) type = L"cover";
  832. bool freebits = false;
  833. if (!mimeType)
  834. {
  835. mimeType = L"jpg"; // TODO: read from ini?
  836. int l = 0;
  837. bits = writeImg((ARGB32*)bits,w,h,&l,mimeType);
  838. if (!bits) return ALBUMART_FAILURE;
  839. freebits = true;
  840. len = l;
  841. }
  842. wchar_t path[MAX_PATH] = {0};
  843. wchar_t fn[MAX_PATH] = {0};
  844. StringCchCopyW(path, MAX_PATH, filename);
  845. PathRemoveFileSpecW(path);
  846. wchar_t albumname[MAX_PATH] = {0};
  847. if (!_wcsicmp(type,L"cover") && AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"album", albumname, MAX_PATH) && albumname[0])
  848. {
  849. CleanNameForPath(albumname);
  850. type = albumname;
  851. }
  852. StringCchPrintfW(fn, MAX_PATH, L"%s\\%s.%s", path, type, mimeType);
  853. int ret = ALBUMART_SUCCESS;
  854. if (bits)
  855. ret = writeFile(fn,bits, (int)len);
  856. if (ret == ALBUMART_SUCCESS)
  857. WASABI_API_SYSCB->syscb_issueCallback(SysCallback::META, MetadataCallback::ART_UPDATED, (intptr_t)fn);
  858. /*
  859. else //bits == NULL, so delete!
  860. _wunlink(fn);
  861. */
  862. if (freebits) WASABI_API_MEMMGR->sysFree((void*)bits);
  863. return ret;
  864. }
  865. static bool CopySceneNFO(const wchar_t *sourcePath, const wchar_t *destinationPath)
  866. {
  867. wchar_t nfo_mask[MAX_PATH] = {0};
  868. PathCombineW(nfo_mask, sourcePath, L"*.nfo");
  869. WIN32_FIND_DATAW find = {0};
  870. HANDLE hFind = FindFirstFileW(nfo_mask, &find);
  871. if (hFind != INVALID_HANDLE_VALUE)
  872. {
  873. wchar_t sourceFile[MAX_PATH] = {0};
  874. wchar_t destinationFile[MAX_PATH] = {0};
  875. PathCombineW(sourceFile, sourcePath, find.cFileName);
  876. PathCombineW(destinationFile, destinationPath, find.cFileName);
  877. CopyFileW(sourceFile, destinationFile, TRUE);
  878. FindClose(hFind);
  879. return true;
  880. }
  881. return false;
  882. }
  883. static void CopyMask(const wchar_t *sourcePath, const wchar_t *destinationPath, const wchar_t *mask)
  884. {
  885. wchar_t findMask[MAX_PATH] = {0};
  886. PathCombineW(findMask, sourcePath, mask);
  887. WIN32_FIND_DATAW find = {0};
  888. HANDLE hFind = FindFirstFileW(findMask, &find);
  889. if (hFind != INVALID_HANDLE_VALUE)
  890. {
  891. do
  892. {
  893. // make sure it's an actual loadable image
  894. waServiceFactory *factory = 0;
  895. svc_imageLoader *loader = FindImageLoader(find.cFileName, &factory);
  896. if (loader)
  897. {
  898. wchar_t sourceFile[MAX_PATH] = {0};
  899. wchar_t destinationFile[MAX_PATH] = {0};
  900. PathCombineW(sourceFile, sourcePath, find.cFileName);
  901. PathCombineW(destinationFile, destinationPath, find.cFileName);
  902. CopyFileW(sourceFile, destinationFile, TRUE);
  903. factory->releaseInterface(loader);
  904. }
  905. }
  906. while (FindNextFileW(hFind, &find));
  907. FindClose(hFind);
  908. }
  909. }
  910. int AlbumArt::CopyAlbumArt(const wchar_t *sourceFilename, const wchar_t *destinationFilename)
  911. {
  912. if (!sourceFilename || !*sourceFilename || !destinationFilename || !*destinationFilename)
  913. return ALBUMART_FAILURE;
  914. if (PathIsURLW(sourceFilename) || PathIsURLW(destinationFilename))
  915. return ALBUMART_FAILURE;
  916. // first, copy embedded album art
  917. waServiceFactory *sourceFactory = 0;
  918. svc_albumArtProvider *sourceProvider = FindProvider(sourceFilename, ALBUMARTPROVIDER_TYPE_EMBEDDED, &sourceFactory);
  919. if (sourceProvider)
  920. {
  921. waServiceFactory *destinationFactory = 0;
  922. svc_albumArtProvider *destinationProvider = FindProvider(destinationFilename, ALBUMARTPROVIDER_TYPE_EMBEDDED, &destinationFactory);
  923. if (destinationProvider)
  924. {
  925. // TODO: iterate through all the different types
  926. void *data = 0;
  927. size_t datalen = 0;
  928. wchar_t *mimeType = 0;
  929. if (sourceProvider->GetAlbumArtData(sourceFilename, L"cover", &data, &datalen, &mimeType) == ALBUMARTPROVIDER_SUCCESS && data && datalen)
  930. {
  931. destinationProvider->SetAlbumArtData(destinationFilename, L"cover", data, datalen, mimeType);
  932. WASABI_API_MEMMGR->sysFree(data);
  933. WASABI_API_MEMMGR->sysFree(mimeType);
  934. }
  935. destinationFactory->releaseInterface(destinationProvider);
  936. }
  937. sourceFactory->releaseInterface(sourceProvider);
  938. }
  939. // now, if they're in different directories, copy folder.jpg, cover.jpg, front.jpg and %album%.jpg
  940. wchar_t sourcePath[MAX_PATH] = {0}, destinationPath[MAX_PATH] = {0};
  941. StringCchCopyW(sourcePath, MAX_PATH, sourceFilename);
  942. PathRemoveFileSpecW(sourcePath);
  943. StringCchCopyW(destinationPath, MAX_PATH, destinationFilename);
  944. PathRemoveFileSpecW(destinationPath);
  945. if (_wcsicmp(sourcePath, destinationPath) != 0) // if they're different
  946. {
  947. CopyMask(sourcePath, destinationPath, L"cover.*");
  948. CopyMask(sourcePath, destinationPath, L"folder.*");
  949. CopyMask(sourcePath, destinationPath, L"front.*");
  950. CopyMask(sourcePath, destinationPath, L"albumart.*");
  951. wchar_t mask[MAX_PATH] = {0};
  952. if (FindSceneNFO(sourcePath, mask))
  953. {
  954. CopyMask(sourcePath, destinationPath, mask);
  955. CopySceneNFO(sourcePath, destinationPath);
  956. }
  957. wchar_t albumname[MAX_PATH] = {0};
  958. if (AGAVE_API_METADATA->GetExtendedFileInfo(sourceFilename, L"album", albumname, MAX_PATH) && albumname[0])
  959. {
  960. CleanNameForPath(albumname);
  961. StringCchPrintfW(mask, MAX_PATH, L"%s.*", albumname);
  962. CopyMask(sourcePath, destinationPath, mask);
  963. }
  964. }
  965. return 0;
  966. }
  967. #define CBCLASS AlbumArt
  968. START_DISPATCH;
  969. CB(API_ALBUMART_GETALBUMART, GetAlbumArt);
  970. CB(API_ALBUMART_GETALBUMART_NOAMG, GetAlbumArt_NoAMG);
  971. CB(API_ALBUMART_GETALBUMARTDATA, GetAlbumArtData);
  972. CB(API_ALBUMART_GETALBUMARTORIGIN, GetAlbumArtOrigin);
  973. CB(API_ALBUMART_GETALBUMARTTYPES, GetAlbumArtTypes);
  974. CB(API_ALBUMART_GETVALIDALBUMARTTYPES, GetValidAlbumArtTypes);
  975. CB(API_ALBUMART_SETALBUMART, SetAlbumArt);
  976. CB(API_ALBUMART_DELETEALBUMART, DeleteAlbumArt);
  977. CB(API_ALBUMART_COPYALBUMART, CopyAlbumArt);
  978. END_DISPATCH;
  979. #undef CBCLASS