Playlists.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. #include <algorithm>
  2. #include "playlists.h"
  3. #include "api__playlist.h"
  4. #include "PlaylistsXML.h"
  5. #include <shlwapi.h>
  6. #include <limits.h>
  7. #include <strsafe.h>
  8. #pragma comment(lib, "Rpcrt4")
  9. using namespace Nullsoft::Utility;
  10. /*
  11. benski> Notes to maintainers
  12. be sure to call DelayLoad() before doing anything.
  13. This is mainly done because the XML parsing service isn't guaranteed to be registered before this service.
  14. It also improves load time.
  15. */
  16. /* --------------------------------------------- */
  17. PlaylistInfo::PlaylistInfo()
  18. {
  19. filename[0] = 0;
  20. title[0] = 0;
  21. length = 0;
  22. numItems = 0;
  23. iTunesID = 0;
  24. cloud = 0;
  25. UuidCreate(&guid);
  26. }
  27. PlaylistInfo::PlaylistInfo( const wchar_t *_filename, const wchar_t *_title, GUID playlist_guid )
  28. {
  29. StringCbCopyW( filename, sizeof( filename ), _filename );
  30. if ( _title )
  31. StringCbCopyW( title, sizeof( title ), _title );
  32. else
  33. title[ 0 ] = 0;
  34. length = 0;
  35. numItems = 0;
  36. if ( playlist_guid == INVALID_GUID )
  37. UuidCreate( &guid );
  38. else
  39. guid = playlist_guid;
  40. iTunesID = 0;
  41. cloud = 0;
  42. }
  43. PlaylistInfo::PlaylistInfo( const PlaylistInfo &copy )
  44. {
  45. StringCbCopyW( filename, sizeof( filename ), copy.filename );
  46. StringCbCopyW( title, sizeof( title ), copy.title );
  47. length = copy.length;
  48. numItems = copy.numItems;
  49. guid = copy.guid;
  50. iTunesID = copy.iTunesID;
  51. cloud = copy.cloud;
  52. }
  53. /* --------------------------------------------- */
  54. Playlists::Playlists()
  55. {
  56. iterator = 0;
  57. triedLoaded = false;
  58. loaded = false;
  59. dirty = false;
  60. }
  61. bool Playlists::DelayLoad()
  62. {
  63. if ( triedLoaded )
  64. return loaded;
  65. PlaylistsXML loader( this );
  66. const wchar_t *g_path = WASABI_API_APP->path_getUserSettingsPath();
  67. wchar_t playlistsFilename[ MAX_PATH ] = { 0 };
  68. wchar_t oldPlaylistsFilename[ MAX_PATH ] = { 0 };
  69. wchar_t newPlaylistsFolder[ MAX_PATH ] = { 0 };
  70. PathCombineW( playlistsFilename, g_path, L"plugins" );
  71. PathAppendW( playlistsFilename, L"ml" );
  72. PathAppendW( playlistsFilename, L"playlists" );
  73. CreateDirectoryW( playlistsFilename, NULL );
  74. lstrcpynW( newPlaylistsFolder, playlistsFilename, MAX_PATH );
  75. PathAppendW( playlistsFilename, L"playlists.xml" );
  76. PathCombineW( oldPlaylistsFilename, g_path, L"plugins" );
  77. PathAppendW( oldPlaylistsFilename, L"ml" );
  78. PathAppendW( oldPlaylistsFilename, L"playlists.xml" );
  79. bool migrated = false;
  80. if ( PathFileExistsW( oldPlaylistsFilename ) && !PathFileExistsW( playlistsFilename ) )
  81. {
  82. if ( MoveFileW( oldPlaylistsFilename, playlistsFilename ) )
  83. {
  84. migrated = true;
  85. PathRemoveFileSpecW( oldPlaylistsFilename );
  86. }
  87. }
  88. switch ( loader.LoadFile( playlistsFilename ) )
  89. {
  90. case PLAYLISTSXML_SUCCESS:
  91. loaded = true;
  92. triedLoaded = true;
  93. if ( AGAVE_API_STATS )
  94. AGAVE_API_STATS->SetStat( api_stats::PLAYLIST_COUNT, (int)playlists.size() );
  95. if ( playlists.size() && migrated )
  96. {
  97. for ( PlaylistInfo l_playlist : playlists )
  98. {
  99. wchar_t path[ MAX_PATH ] = { 0 }, file[ MAX_PATH ] = { 0 };
  100. lstrcpynW( file, l_playlist.filename, MAX_PATH );
  101. PathStripPathW( file );
  102. PathCombineW( path, oldPlaylistsFilename, file );
  103. if ( PathFileExistsW( path ) )
  104. {
  105. wchar_t new_path[ MAX_PATH ] = { 0 };
  106. PathCombineW( new_path, newPlaylistsFolder, file );
  107. MoveFileW( path, new_path );
  108. }
  109. }
  110. dirty = true;
  111. Flush();
  112. }
  113. break;
  114. case PLAYLISTSXML_NO_PARSER:
  115. // if there's XML parser, we'll try again on the off-chance it eventually gets loaded (we might still be in the midst of loading the w5s/wac components)
  116. break;
  117. default:
  118. loaded = true;
  119. triedLoaded = true;
  120. break;
  121. }
  122. return loaded;
  123. }
  124. void Playlists::Lock()
  125. {
  126. playlistsGuard.Lock();
  127. }
  128. void Playlists::Unlock()
  129. {
  130. playlistsGuard.Unlock();
  131. }
  132. size_t Playlists::GetIterator()
  133. {
  134. return iterator;
  135. }
  136. static void WriteEscaped( FILE *fp, const wchar_t *str )
  137. {
  138. // TODO: for speed optimization,
  139. // we should wait until we hit a special character
  140. // and write out everything else so before it,
  141. // like how ASX loader does it
  142. while ( str && *str )
  143. {
  144. switch ( *str )
  145. {
  146. case L'&':
  147. fputws( L"&amp;", fp );
  148. break;
  149. case L'>':
  150. fputws( L"&gt;", fp );
  151. break;
  152. case L'<':
  153. fputws( L"&lt;", fp );
  154. break;
  155. case L'\'':
  156. fputws( L"&apos;", fp );
  157. break;
  158. case L'\"':
  159. fputws( L"&quot;", fp );
  160. break;
  161. default:
  162. fputwc( *str, fp );
  163. break;
  164. }
  165. // write out the whole UTF-16 character
  166. wchar_t *next = CharNextW( str );
  167. while ( ++str != next )
  168. fputwc( *str, fp );
  169. }
  170. }
  171. bool TitleSortAsc( PlaylistInfo &item1, PlaylistInfo &item2 )
  172. {
  173. int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREWIDTH, item1.title, -1, item2.title, -1 );
  174. return comp == CSTR_LESS_THAN;
  175. }
  176. bool TitleSortDesc( PlaylistInfo &item1, PlaylistInfo &item2 )
  177. {
  178. int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREWIDTH, item1.title, -1, item2.title, -1 );
  179. return comp == CSTR_GREATER_THAN;
  180. }
  181. bool NumberOfEntrySortAsc( PlaylistInfo &item1, PlaylistInfo &item2 )
  182. {
  183. return !!( item1.numItems < item2.numItems );
  184. }
  185. bool NumberOfEntrySortDesc( PlaylistInfo &item1, PlaylistInfo &item2 )
  186. {
  187. return !!( item1.numItems > item2.numItems );
  188. }
  189. int Playlists::Sort( size_t sort_type )
  190. {
  191. if ( !DelayLoad() )
  192. return 0;
  193. int sorted = 1;
  194. switch ( sort_type )
  195. {
  196. case SORT_TITLE_ASCENDING:
  197. std::sort( playlists.begin(), playlists.end(), TitleSortAsc );
  198. break;
  199. case SORT_TITLE_DESCENDING:
  200. std::sort( playlists.begin(), playlists.end(), TitleSortDesc );
  201. break;
  202. case SORT_NUMBER_ASCENDING:
  203. std::sort( playlists.begin(), playlists.end(), NumberOfEntrySortAsc );
  204. break;
  205. case SORT_NUMBER_DESCENDING:
  206. std::sort( playlists.begin(), playlists.end(), NumberOfEntrySortDesc );
  207. break;
  208. default:
  209. sorted = 0;
  210. break;
  211. }
  212. dirty = true;
  213. if ( sorted )
  214. Flush();
  215. return sorted;
  216. }
  217. void Playlists::Flush()
  218. {
  219. AutoLockT<Playlists> lock( this );
  220. if ( !triedLoaded && !loaded ) // if the playlists.xml file was never even attempted to be loaded, don't overwrite
  221. return;
  222. if ( !dirty ) // if we've not seen any changes then no need to re-save
  223. return;
  224. const wchar_t *g_path = WASABI_API_APP->path_getUserSettingsPath();
  225. wchar_t rootPath[ MAX_PATH ] = { 0 };
  226. wchar_t playlistsBackupFilename[ MAX_PATH ] = { 0 };
  227. wchar_t playlistsDestination[ MAX_PATH ] = { 0 };
  228. PathCombineW( rootPath, g_path, L"plugins" );
  229. CreateDirectoryW( rootPath, NULL );
  230. PathAppendW( rootPath, L"ml" );
  231. CreateDirectoryW( rootPath, NULL );
  232. PathAppendW( rootPath, L"playlists" );
  233. CreateDirectoryW( rootPath, NULL );
  234. int g_path_size = wcslen( rootPath );
  235. PathCombineW( playlistsBackupFilename, rootPath, L"playlists.xml.backup" );
  236. PathCombineW( playlistsDestination, rootPath, L"playlists.xml" );
  237. CopyFileW( playlistsDestination, playlistsBackupFilename, FALSE );
  238. FILE *fp = _wfopen( playlistsDestination, L"wb" );
  239. if ( !fp ) // bah
  240. {
  241. dirty = false;
  242. return;
  243. }
  244. fseek( fp, 0, SEEK_SET );
  245. fputwc( L'\xFEFF', fp );
  246. fwprintf( fp, L"<?xml version=\"1.0\" encoding=\"UTF-16\"?>" );
  247. fwprintf( fp, L"<playlists playlists=\"%u\">", (unsigned int)playlists.size() );
  248. if ( AGAVE_API_STATS )
  249. AGAVE_API_STATS->SetStat( api_stats::PLAYLIST_COUNT, (int)playlists.size() );
  250. for ( PlaylistInfo &l_play_list_info : playlists )
  251. {
  252. fputws( L"<playlist filename=\"", fp );
  253. const wchar_t *fn = l_play_list_info.filename;
  254. if ( !_wcsnicmp( rootPath, fn, g_path_size ) )
  255. {
  256. fn += g_path_size;
  257. if ( *fn == L'\\' )
  258. ++fn;
  259. }
  260. WriteEscaped( fp, fn );
  261. fputws( L"\" title=\"", fp );
  262. WriteEscaped( fp, l_play_list_info.title );
  263. GUID guid = l_play_list_info.guid;
  264. fwprintf( fp, L"\" id=\"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\"",
  265. (int)guid.Data1, (int)guid.Data2, (int)guid.Data3,
  266. (int)guid.Data4[ 0 ], (int)guid.Data4[ 1 ],
  267. (int)guid.Data4[ 2 ], (int)guid.Data4[ 3 ],
  268. (int)guid.Data4[ 4 ], (int)guid.Data4[ 5 ],
  269. (int)guid.Data4[ 6 ], (int)guid.Data4[ 7 ] );
  270. fwprintf( fp, L" songs=\"%u\" seconds=\"%u\"", l_play_list_info.numItems, l_play_list_info.length );
  271. if ( l_play_list_info.iTunesID )
  272. fwprintf( fp, L" iTunesID=\"%I64u\"", l_play_list_info.iTunesID );
  273. if ( l_play_list_info.cloud )
  274. fwprintf( fp, L" cloud=\"1\"" );
  275. fwprintf( fp, L"/>" );
  276. }
  277. fwprintf( fp, L"</playlists>" );
  278. fclose( fp );
  279. dirty = false;
  280. }
  281. size_t Playlists::GetCount()
  282. {
  283. DelayLoad();
  284. return playlists.size();
  285. }
  286. const wchar_t *Playlists::GetFilename( size_t index )
  287. {
  288. if ( !DelayLoad() || index >= playlists.size() )
  289. return 0;
  290. return playlists[ index ].filename;
  291. }
  292. const wchar_t *Playlists::GetName( size_t index )
  293. {
  294. if ( !DelayLoad() || index >= playlists.size() )
  295. return 0;
  296. return playlists[ index ].title;
  297. }
  298. GUID Playlists::GetGUID( size_t index )
  299. {
  300. if ( !DelayLoad() || index >= playlists.size() )
  301. return INVALID_GUID;
  302. return playlists[ index ].guid;
  303. }
  304. int Playlists::GetPosition( GUID playlist_guid, size_t *index )
  305. {
  306. if ( !DelayLoad() )
  307. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  308. size_t indexCount = 0;
  309. for ( PlaylistInfo &l_play_list_info : playlists )
  310. {
  311. if ( l_play_list_info.guid == playlist_guid )
  312. {
  313. *index = indexCount;
  314. return API_PLAYLISTS_SUCCESS;
  315. }
  316. ++indexCount;
  317. }
  318. return API_PLAYLISTS_FAILURE;
  319. }
  320. template <class val_t>
  321. static int GetWithSize( void *data, size_t dataLen, val_t value )
  322. {
  323. switch ( dataLen )
  324. {
  325. case 1:
  326. {
  327. if ( value > _UI8_MAX ) // check for overflow
  328. return API_PLAYLISTS_BAD_SIZE;
  329. *(uint8_t *)data = (uint8_t)value;
  330. return API_PLAYLISTS_SUCCESS;
  331. }
  332. case 2:
  333. {
  334. if ( value > _UI16_MAX ) // check for overflow
  335. return API_PLAYLISTS_BAD_SIZE;
  336. *(uint16_t *)data = (uint16_t)value;
  337. return API_PLAYLISTS_SUCCESS;
  338. }
  339. case 4:
  340. {
  341. if ( value > _UI32_MAX )
  342. return API_PLAYLISTS_BAD_SIZE;
  343. *(uint32_t *)data = (uint32_t)value;
  344. return API_PLAYLISTS_SUCCESS;
  345. }
  346. case 8:
  347. {
  348. if ( value > _UI64_MAX )
  349. return API_PLAYLISTS_BAD_SIZE;
  350. *(uint64_t *)data = (uint64_t)value;
  351. return API_PLAYLISTS_SUCCESS;
  352. }
  353. }
  354. return API_PLAYLISTS_BAD_SIZE;
  355. }
  356. template <class val_t>
  357. static int SetWithSize( void *data, size_t dataLen, val_t *value )
  358. {
  359. switch ( dataLen )
  360. {
  361. case 1:
  362. {
  363. *value = ( val_t ) * (uint8_t *)data;
  364. return API_PLAYLISTS_SUCCESS;
  365. }
  366. case 2:
  367. {
  368. *value = ( val_t ) * (uint16_t *)data;
  369. return API_PLAYLISTS_SUCCESS;
  370. }
  371. case 4:
  372. {
  373. *value = ( val_t ) * (uint32_t *)data;
  374. return API_PLAYLISTS_SUCCESS;
  375. }
  376. case 8:
  377. {
  378. *value = ( val_t ) * (uint64_t *)data;
  379. return API_PLAYLISTS_SUCCESS;
  380. }
  381. }
  382. return API_PLAYLISTS_BAD_SIZE;
  383. }
  384. int Playlists::GetInfo( size_t index, GUID info, void *data, size_t dataLen )
  385. {
  386. if ( !DelayLoad() )
  387. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  388. if ( index >= playlists.size() )
  389. return API_PLAYLISTS_INVALID_INDEX;
  390. if ( info == api_playlists_itemCount )
  391. return GetWithSize( data, dataLen, playlists[ index ].numItems );
  392. else if ( info == api_playlists_totalTime )
  393. return GetWithSize( data, dataLen, playlists[ index ].length );
  394. else if ( info == api_playlists_iTunesID )
  395. return GetWithSize( data, dataLen, playlists[ index ].iTunesID );
  396. else if ( info == api_playlists_cloud )
  397. return GetWithSize( data, dataLen, playlists[ index ].cloud );
  398. return API_PLAYLISTS_UNKNOWN_INFO_GUID;
  399. }
  400. int Playlists::MoveBefore( size_t index1, size_t index2 )
  401. {
  402. if ( !DelayLoad() )
  403. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  404. if ( index1 >= playlists.size() )
  405. return API_PLAYLISTS_INVALID_INDEX;
  406. PlaylistInfo copy = playlists[ index1 ];
  407. if ( index2 >= playlists.size() )
  408. {
  409. playlists.push_back( copy );
  410. playlists.erase(playlists.begin() + index1 );
  411. }
  412. else
  413. {
  414. playlists.insert(playlists.begin() + index2, copy );
  415. if ( index1 >= index2 )
  416. index1++;
  417. playlists.erase(playlists.begin() + index1 );
  418. }
  419. dirty = true;
  420. ++iterator;
  421. return API_PLAYLISTS_SUCCESS;
  422. }
  423. size_t Playlists::AddPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid )
  424. {
  425. if ( !DelayLoad() )
  426. return -1;
  427. AutoLockT<Playlists> lock( this );
  428. if ( playlist_guid != INVALID_GUID )
  429. {
  430. for ( size_t index = 0; index < playlists.size(); index++ )
  431. {
  432. if ( playlists[ index ].guid == playlist_guid )
  433. {
  434. if ( lstrcmpiW( playlists[ index ].title, playlistName ) )
  435. {
  436. dirty = true;
  437. StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), playlistName );
  438. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 );
  439. }
  440. return -2;
  441. }
  442. }
  443. }
  444. size_t newIndex = AddPlaylist_NoCallback( filename, playlistName, playlist_guid );
  445. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, newIndex, 0 );
  446. return newIndex;
  447. }
  448. size_t Playlists::AddCloudPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid )
  449. {
  450. if ( !DelayLoad() )
  451. return -1;
  452. AutoLockT<Playlists> lock( this );
  453. if ( playlist_guid != INVALID_GUID )
  454. {
  455. for ( size_t index = 0; index < playlists.size(); index++ )
  456. {
  457. if ( playlists[ index ].guid == playlist_guid )
  458. {
  459. // we make sure that this playlist has a 'cloud' flag
  460. // as without it, our detection of thing isn't ideal.
  461. if ( !playlists[ index ].cloud )
  462. playlists[ index ].cloud = 1;
  463. if ( lstrcmpW( playlists[ index ].title, playlistName ) )
  464. {
  465. dirty = true;
  466. StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), playlistName );
  467. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 );
  468. }
  469. return -2;
  470. }
  471. }
  472. }
  473. size_t newIndex = AddPlaylist_NoCallback( filename, playlistName, playlist_guid );
  474. int cloud = 1;
  475. SetInfo( newIndex, api_playlists_cloud, &cloud, sizeof( cloud ) );
  476. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, newIndex, 0 );
  477. return newIndex;
  478. }
  479. size_t Playlists::AddPlaylist_internal( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid, size_t numItems, size_t length, uint64_t iTunesID, size_t cloud )
  480. {
  481. PlaylistInfo newPlaylist( filename, playlistName, playlist_guid );
  482. newPlaylist.numItems = (int)numItems;
  483. newPlaylist.length = (int)length;
  484. newPlaylist.iTunesID = iTunesID;
  485. newPlaylist.cloud = (int)cloud;
  486. playlists.push_back( newPlaylist );
  487. size_t newIndex = playlists.size() - 1;
  488. return newIndex;
  489. }
  490. size_t Playlists::AddPlaylist_NoCallback( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid )
  491. {
  492. AutoLockT<Playlists> lock( this );
  493. PlaylistInfo newPlaylist( filename, playlistName, playlist_guid );
  494. dirty = true;
  495. playlists.push_back( newPlaylist );
  496. ++iterator;
  497. size_t newIndex = playlists.size() - 1;
  498. return newIndex;
  499. }
  500. int Playlists::SetGUID( size_t index, GUID playlist_guid )
  501. {
  502. if ( !DelayLoad() )
  503. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  504. if ( index >= playlists.size() )
  505. return API_PLAYLISTS_INVALID_INDEX;
  506. dirty = true;
  507. playlists[ index ].guid = playlist_guid;
  508. return API_PLAYLISTS_SUCCESS;
  509. }
  510. int Playlists::RenamePlaylist( size_t index, const wchar_t *name )
  511. {
  512. if ( !DelayLoad() )
  513. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  514. if ( index >= playlists.size() )
  515. return API_PLAYLISTS_INVALID_INDEX;
  516. dirty = true;
  517. if ( lstrcmpW( playlists[ index ].title, name ) )
  518. {
  519. StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), name );
  520. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 );
  521. }
  522. return API_PLAYLISTS_SUCCESS;
  523. }
  524. int Playlists::MovePlaylist( size_t index, const wchar_t *filename )
  525. {
  526. if ( !DelayLoad() )
  527. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  528. if ( index >= playlists.size() )
  529. return API_PLAYLISTS_INVALID_INDEX;
  530. dirty = true;
  531. StringCbCopyW( playlists[ index ].filename, sizeof( playlists[ index ].filename ), filename );
  532. iterator++;
  533. return API_PLAYLISTS_SUCCESS;
  534. }
  535. int Playlists::SetInfo( size_t index, GUID info, void *data, size_t dataLen )
  536. {
  537. if ( !DelayLoad() )
  538. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  539. if ( index >= playlists.size() )
  540. return API_PLAYLISTS_INVALID_INDEX;
  541. dirty = true;
  542. if ( info == api_playlists_itemCount )
  543. return SetWithSize( data, dataLen, &playlists[ index ].numItems );
  544. else if ( info == api_playlists_totalTime )
  545. return SetWithSize( data, dataLen, &playlists[ index ].length );
  546. else if ( info == api_playlists_iTunesID )
  547. return SetWithSize( data, dataLen, &playlists[ index ].iTunesID );
  548. else if ( info == api_playlists_cloud )
  549. return SetWithSize( data, dataLen, &playlists[ index ].cloud );
  550. dirty = false;
  551. return API_PLAYLISTS_UNKNOWN_INFO_GUID;
  552. }
  553. int Playlists::RemovePlaylist( size_t index )
  554. {
  555. if ( !DelayLoad() )
  556. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  557. if ( index >= playlists.size() )
  558. return API_PLAYLISTS_INVALID_INDEX;
  559. dirty = true;
  560. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_REMOVED_PRE, index, 0 );
  561. playlists.erase(playlists.begin() + index );
  562. iterator++;
  563. WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_REMOVED_POST, index, 0 );
  564. return API_PLAYLISTS_SUCCESS;
  565. }
  566. int Playlists::ClearPlaylists()
  567. {
  568. AutoLockT<Playlists> lock( this );
  569. if ( !DelayLoad() )
  570. return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS;
  571. dirty = true;
  572. playlists.clear();
  573. return API_PLAYLISTS_SUCCESS;
  574. }
  575. const PlaylistInfo &Playlists::GetPlaylistInfo(size_t i)
  576. {
  577. return playlists[i];
  578. }
  579. #define CBCLASS Playlists
  580. START_DISPATCH;
  581. VCB( API_PLAYLISTS_LOCK, Lock );
  582. VCB( API_PLAYLISTS_UNLOCK, Unlock );
  583. CB( API_PLAYLISTS_GETITERATOR, GetIterator );
  584. VCB( API_PLAYLISTS_FLUSH, Flush );
  585. CB( API_PLAYLISTS_GETCOUNT, GetCount );
  586. CB( API_PLAYLISTS_GETFILENAME, GetFilename );
  587. CB( API_PLAYLISTS_GETNAME, GetName );
  588. CB( API_PLAYLISTS_GETGUID, GetGUID );
  589. CB( API_PLAYLISTS_GETPOSITION, GetPosition );
  590. CB( API_PLAYLISTS_GETINFO, GetInfo );
  591. CB( API_PLAYLISTS_MOVEBEFORE, MoveBefore );
  592. CB( API_PLAYLISTS_ADDPLAYLIST, AddPlaylist );
  593. CB( API_PLAYLISTS_ADDPLAYLISTNOCB, AddPlaylist_NoCallback );
  594. CB( API_PLAYLISTS_ADDCLOUDPLAYLIST, AddCloudPlaylist );
  595. CB( API_PLAYLISTS_SETGUID, SetGUID );
  596. CB( API_PLAYLISTS_RENAMEPLAYLIST, RenamePlaylist );
  597. CB( API_PLAYLISTS_MOVEPLAYLIST, MovePlaylist );
  598. CB( API_PLAYLISTS_SETINFO, SetInfo );
  599. CB( API_PLAYLISTS_REMOVEPLAYLIST, RemovePlaylist );
  600. CB( API_PLAYLISTS_CLEARPLAYLISTS, ClearPlaylists );
  601. CB( API_PLAYLISTS_SORT, Sort );
  602. END_DISPATCH;
  603. #undef CBCLASS