DeviceView.cpp 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714
  1. //#define _WIN32_WINNT 0x0400
  2. #include "../Winamp/buildType.h"
  3. #include "main.h"
  4. #include "DeviceView.h"
  5. //#include <commctrl.h>
  6. #include "nu/AutoWide.h"
  7. #include "nu/AutoChar.h"
  8. #include "../nu/AutoUrl.h"
  9. #include "SkinnedListView.h"
  10. #include "../playlist/api_playlistmanager.h"
  11. #include "../playlist/ifc_playlistdirectorycallback.h"
  12. #include "../playlist/ifc_playlistloadercallback.h"
  13. #include "api__ml_pmp.h"
  14. #include <shlwapi.h>
  15. #include <time.h>
  16. #include "metadata_utils.h"
  17. #include "../ml_wire/ifc_podcast.h"
  18. #include "./local_menu.h"
  19. #include "IconStore.h"
  20. #include "../devices/ifc_deviceevent.h"
  21. #include "metadata_utils.h"
  22. #include "../nu/sort.h"
  23. #include "resource1.h"
  24. #include <strsafe.h>
  25. #include "../nu/MediaLibraryInterface.h"
  26. extern C_ItemList devices;
  27. extern C_Config * global_config;
  28. extern INT_PTR CALLBACK pmp_artistalbum_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  29. extern INT_PTR CALLBACK pmp_playlist_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  30. int IPC_GET_CLOUD_HINST = -1, IPC_LIBRARY_PLAYLISTS_REFRESH = -1;
  31. HINSTANCE cloud_hinst = 0;
  32. int currentViewedPlaylist=0;
  33. HNAVITEM cloudQueueTreeItem=NULL;
  34. LinkedQueue cloudTransferQueue, cloudFinishedTransfers;
  35. int cloudTransferProgress = 0;
  36. DeviceView * currentViewedDevice=NULL;
  37. volatile size_t TransferContext::paused_all = 0;
  38. extern void UpdateTransfersListView(bool softUpdate, CopyInst * item=NULL);
  39. extern void UpdateDevicesListView(bool softUpdate);
  40. extern INT_PTR CALLBACK config_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  41. extern HWND mainMessageWindow;
  42. extern prefsDlgRecW prefsPage;
  43. extern int prefsPageLoaded;
  44. static int thread_id;
  45. static bool copySettings(wchar_t * ssrc, wchar_t * sdest);
  46. static __int64 fileSize(wchar_t * filename);
  47. static void removebadchars(wchar_t *s);
  48. extern ThreadID *transfer_thread;
  49. int TransferThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id);
  50. INT_PTR CALLBACK pmp_queue_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  51. INT_PTR CALLBACK pmp_video_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  52. DeviceView::DeviceView(Device *dev)
  53. : activityRunning(FALSE), navigationItemCreated(FALSE), usedSpace(0), totalSpace(0)
  54. {
  55. memset(name, 0, sizeof(name));
  56. queueActiveIcon = isCloudDevice = 0;
  57. treeItem = videoTreeItem = queueTreeItem = 0;
  58. connection_type = "USB";
  59. display_type = "Portable Media Player";
  60. metadata_fields = (int)dev->extraActions(DEVICE_SUPPORTED_METADATA,0,0,0);
  61. if(!metadata_fields)
  62. metadata_fields = -1;
  63. dev->extraActions(DEVICE_GET_CONNECTION_TYPE, (intptr_t)&connection_type, 0, 0);
  64. isCloudDevice = (!lstrcmpiA(connection_type, "cloud"));
  65. dev->extraActions(DEVICE_GET_DISPLAY_TYPE, (intptr_t)&display_type, 0, 0);
  66. ref_count = 1;
  67. if (dev->extraActions(DEVICE_GET_UNIQUE_ID, (intptr_t)name, sizeof(name), 0) == 0)
  68. {
  69. // fallback
  70. GUID name_guid;
  71. CoCreateGuid(&name_guid);
  72. StringCbPrintfA(name, sizeof(name), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  73. (int)name_guid.Data1, (int)name_guid.Data2, (int)name_guid.Data3,
  74. (int)name_guid.Data4[0], (int)name_guid.Data4[1],
  75. (int)name_guid.Data4[2], (int)name_guid.Data4[3],
  76. (int)name_guid.Data4[4], (int)name_guid.Data4[5],
  77. (int)name_guid.Data4[6], (int)name_guid.Data4[7] );
  78. }
  79. wchar_t inifile[MAX_PATH] = {0};
  80. dev->extraActions(DEVICE_GET_INI_FILE,(intptr_t)inifile,0,0);
  81. if(!inifile[0])
  82. {
  83. wchar_t name[256] = {0};
  84. dev->getPlaylistName(0,name,256);
  85. removebadchars(name);
  86. // build this slow so we make sure each directory exists
  87. PathCombine(inifile, WASABI_API_APP->path_getUserSettingsPath(), L"Plugins");
  88. CreateDirectory(inifile, NULL);
  89. PathAppend(inifile, L"ml");
  90. CreateDirectory(inifile, NULL);
  91. wchar_t ini_filespec[MAX_PATH] = {0};
  92. StringCchPrintf(ini_filespec, MAX_PATH, L"ml_pmp_device_%s.ini",name);
  93. PathAppend(inifile, ini_filespec);
  94. }
  95. if(fileSize(inifile) <= 0) copySettings(global_config->GetIniFile(),inifile); // import old settings
  96. config = new C_Config(inifile,L"ml_pmp",global_config);
  97. currentTransferProgress = 0;
  98. transferRate=0;
  99. commitNeeded=false;
  100. this->dev = dev;
  101. wchar_t deviceName[256]=L"";
  102. dev->getPlaylistName(0,deviceName,sizeof(deviceName)/sizeof(wchar_t));
  103. if (!isCloudDevice) videoView = config->ReadInt(L"showVideoView",dev->extraActions(DEVICE_SUPPORTS_VIDEO,0,0,0));
  104. else videoView = 0;
  105. prefsDlgRecW *parentPrefs = (prefsDlgRecW *)dev->extraActions(DEVICE_GET_PREFS_PARENT, 0, 0, 0);
  106. if (!parentPrefs)
  107. {
  108. // only add it when we're using our own root page, otherwise skip this
  109. if (!prefsPageLoaded)
  110. {
  111. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&prefsPage,IPC_ADD_PREFS_DLGW);
  112. }
  113. prefsPageLoaded+=1;
  114. }
  115. if (lstrcmpi(deviceName, L"all_sources"))
  116. {
  117. devPrefsPage.hInst=WASABI_API_LNG_HINST;
  118. devPrefsPage.where=(parentPrefs ? (intptr_t)parentPrefs : (intptr_t)&prefsPage);
  119. devPrefsPage.dlgID=IDD_CONFIG;
  120. devPrefsPage.name=_wcsdup(deviceName);
  121. devPrefsPage.proc=config_dlgproc;
  122. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&devPrefsPage,IPC_ADD_PREFS_DLGW);
  123. }
  124. else
  125. {
  126. memset(&devPrefsPage, 0, sizeof(prefsDlgRecW));
  127. }
  128. UpdateSpaceInfo(TRUE, FALSE);
  129. threadKillswitch = 0;
  130. transferContext.transfer_thread = WASABI_API_THREADPOOL->ReserveThread(api_threadpool::FLAG_LONG_EXECUTION);
  131. transferContext.dev = this;
  132. WASABI_API_THREADPOOL->AddHandle(transferContext.transfer_thread, transferContext.notifier, TransferThreadPoolFunc, &transferContext, thread_id, api_threadpool::FLAG_LONG_EXECUTION);
  133. thread_id++;
  134. if (AGAVE_API_DEVICEMANAGER)
  135. {
  136. ifc_device *registered_device = this;
  137. AGAVE_API_DEVICEMANAGER->DeviceRegister(&registered_device, 1);
  138. }
  139. //hTransferThread = CreateThread(NULL, 0, ThreadFunc_Transfer, (LPVOID)this, 0, &dwThreadId);
  140. /*
  141. if(dev->getDeviceCapacityTotal() > L3000000000) SyncConnectionDefault=1;
  142. else SyncConnectionDefault=2;
  143. */
  144. SyncConnectionDefault=0; // default off for now.
  145. if (!isCloudDevice)
  146. {
  147. // ok all started. Now do any "on connect" actions...
  148. time_t lastSync = (time_t)config->ReadInt(L"syncOnConnect_time",0);
  149. time_t now = time(NULL);
  150. //int diff = now - lastSync;
  151. double diff = difftime(now,lastSync);
  152. config->WriteInt(L"syncOnConnect_time",(int)now);
  153. if(diff > config->ReadInt(L"syncOnConnect_hours",12)*3600)
  154. {
  155. switch(config->ReadInt(L"syncOnConnect",SyncConnectionDefault))
  156. {
  157. case 1:
  158. {
  159. if (!isCloudDevice) Sync(true);
  160. //else CloudSync(true);
  161. }
  162. break;
  163. case 2: Autofill(); break;
  164. }
  165. }
  166. }
  167. if (!AGAVE_API_DEVICEMANAGER)
  168. RegisterViews(0);
  169. }
  170. HNAVITEM GetNavigationRoot(BOOL forceCreate);
  171. HNAVITEM NavigationItem_Find(HNAVITEM root, const wchar_t *name, BOOL allow_root = 0);
  172. void DeviceView::RegisterViews(HNAVITEM parent)
  173. {
  174. NAVINSERTSTRUCT nis = {0};
  175. NAVITEM *item = 0;
  176. wchar_t buffer[128] = {0};
  177. item = &nis.item;
  178. if(!parent)
  179. {
  180. MLTREEIMAGE devIcon;
  181. wchar_t deviceName[256]=L"";
  182. devIcon.resourceId = IDR_DEVICE_ICON;
  183. devIcon.hinst = plugin.hDllInstance;
  184. dev->extraActions(DEVICE_SET_ICON,(intptr_t)&devIcon,0,0);
  185. dev->getPlaylistName(0,deviceName,sizeof(deviceName)/sizeof(wchar_t));
  186. nis.hParent = GetNavigationRoot(TRUE);
  187. nis.hInsertAfter = NCI_LAST;
  188. item->cbSize = sizeof(NAVITEM);
  189. item->mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_STYLE | NIMF_IMAGE | NIMF_IMAGESEL;
  190. item->pszText = deviceName;
  191. item->pszInvariant = nis.item.pszText;
  192. item->style = NIS_HASCHILDREN;
  193. item->styleMask = item->style,
  194. item->iImage = icon_store.GetResourceIcon(devIcon.hinst, MAKEINTRESOURCE(devIcon.resourceId));
  195. item->iSelectedImage = item->iImage;
  196. treeItem = parent = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  197. navigationItemCreated = TRUE;
  198. }
  199. else
  200. {
  201. treeItem = parent;
  202. navigationItemCreated = FALSE;
  203. item->cbSize = sizeof(NAVITEM);
  204. item->mask = NIMF_STYLE;
  205. item->hItem = treeItem;
  206. item->style = NIS_HASCHILDREN;
  207. item->styleMask = NIS_HASCHILDREN;
  208. MLNavItem_SetInfo(plugin.hwndLibraryParent, item);
  209. /* create transfer view */
  210. // TODO: create this view dynamically
  211. HNAVITEM cloud = 0;
  212. if (isCloudDevice)
  213. {
  214. cloud = NavigationItem_Find(0, L"cloud_sources", TRUE);
  215. if (cloud != NULL) parent = cloud;
  216. }
  217. #if 0
  218. int mode = gen_mlconfig->ReadInt(L"txviewpos", 0);
  219. if (mode == 1)
  220. {
  221. nis.hParent = cloud;//parent;
  222. nis.hInsertAfter = NCI_FIRST;
  223. }
  224. else if (mode == 2)
  225. {
  226. nis.hParent = NavigationItem_Find(0, L"ml_devices_root", TRUE);
  227. nis.hInsertAfter = NCI_FIRST;
  228. }
  229. #else
  230. nis.hParent = parent;
  231. #endif
  232. item->cbSize = sizeof(NAVITEM);
  233. item->mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL;
  234. item->pszText = WASABI_API_LNGSTRINGW_BUF(IDS_TRANSFERS, buffer, 128);
  235. item->pszInvariant = (isCloudDevice ? L"cloud_transfers" : L"transfers");
  236. item->iImage = icon_store.GetQueueIcon();
  237. item->iSelectedImage = nis.item.iImage;
  238. #if 0
  239. if (!cloudQueueTreeItem && isCloudDevice)
  240. {
  241. NAVINSERTSTRUCT nis2 = {0};
  242. nis2.item.cbSize = sizeof(NAVITEM);
  243. nis2.item.pszText = WASABI_API_LNGSTRINGW(IDS_ADD_SOURCE);
  244. nis2.item.pszInvariant = L"cloud_add_sources";
  245. nis2.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE;
  246. nis2.item.iImage = nis2.item.iSelectedImage = mediaLibrary.AddTreeImageBmp(IDB_TREEITEM_CLOUD_ADD_SOURCE);
  247. nis2.hParent = parent;
  248. nis2.hInsertAfter = NCI_FIRST;
  249. HNAVITEM item = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis2);
  250. if (mode == 1) nis.hInsertAfter = item;
  251. queueTreeItem = cloudQueueTreeItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  252. #if 0
  253. nis2.item.pszText = L"BYOS";//WASABI_API_LNGSTRINGW(IDS_ADD_SOURCE);
  254. nis2.item.pszInvariant = L"cloud_byos";
  255. nis2.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE;
  256. nis2.item.iImage = nis2.item.iSelectedImage = mediaLibrary.AddTreeImageBmp(IDB_TREEITEM_CLOUD_ADD_BYOS);
  257. nis2.hParent = parent;
  258. nis2.hInsertAfter = nis.hInsertAfter;
  259. MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis2);
  260. #endif
  261. }
  262. else
  263. queueTreeItem = cloudQueueTreeItem;
  264. #endif
  265. queueTreeItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  266. }
  267. /* create video view */
  268. if (videoView)
  269. {
  270. nis.hParent = parent;
  271. nis.hInsertAfter = NCI_LAST;
  272. item->cbSize = sizeof(NAVITEM);
  273. item->mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL;
  274. item->pszText = WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO, buffer, 128);
  275. item->pszInvariant = L"video";
  276. item->iImage = icon_store.GetVideoIcon();
  277. item->iSelectedImage = nis.item.iImage;
  278. videoTreeItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  279. }
  280. else
  281. videoTreeItem = 0;
  282. /* create playlists */
  283. int l = (dev ? dev->getPlaylistCount() : 0);
  284. for(int i=1; i<l; i++)
  285. {
  286. AddPlaylistNode(i);
  287. }
  288. }
  289. DeviceView::~DeviceView()
  290. {
  291. if(configDevice == this) SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&prefsPage,IPC_OPENPREFSTOPAGE);
  292. // remove it when we're removed what we added
  293. int lastPrefsPageLoaded = prefsPageLoaded;
  294. prefsPageLoaded-=1;
  295. if(lastPrefsPageLoaded == 1)
  296. {
  297. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&prefsPage,IPC_REMOVE_PREFS_DLG);
  298. }
  299. //OutputDebugString(L"device unloading started");
  300. // get rid of the transfer thread
  301. threadKillswitch=1;
  302. transferContext.WaitForKill();
  303. if(threadKillswitch != 100)
  304. {
  305. /*OutputDebugString(L"FUCKO");*/
  306. }
  307. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&devPrefsPage,IPC_REMOVE_PREFS_DLG);
  308. free(devPrefsPage.name);
  309. //OutputDebugString(L"device unloading finished");
  310. delete config;
  311. }
  312. void DeviceView::SetVideoView(BOOL enabled)
  313. {
  314. videoView=enabled;
  315. config->WriteInt(L"showVideoView",videoView);
  316. if(videoView)
  317. {
  318. /* add video before the playlists */
  319. wchar_t buffer[128] = {0};
  320. NAVINSERTSTRUCT nis = {0};
  321. nis.item.cbSize = sizeof(NAVITEM);
  322. nis.item.pszText = WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO, buffer, 128);
  323. nis.item.pszInvariant = L"video";
  324. nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL;
  325. nis.hParent = treeItem;
  326. nis.hInsertAfter = NCI_FIRST;
  327. nis.item.iImage = icon_store.GetVideoIcon();
  328. nis.item.iSelectedImage = nis.item.iImage;
  329. videoTreeItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  330. }
  331. else
  332. {
  333. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, videoTreeItem);
  334. videoTreeItem = 0;
  335. }
  336. }
  337. static void removebadchars(wchar_t *s)
  338. {
  339. while (s && *s)
  340. {
  341. if (*s == L'?' || *s == L'/' || *s == L'\\' || *s == L':' || *s == L'*' || *s == L'\"' || *s == L'<' || *s == L'>' || *s == L'|')
  342. *s = L'_';
  343. s = CharNextW(s);
  344. }
  345. }
  346. static __int64 fileSize(wchar_t * filename)
  347. {
  348. WIN32_FIND_DATA f= {0};
  349. HANDLE h = FindFirstFileW(filename,&f);
  350. if(h == INVALID_HANDLE_VALUE) return -1;
  351. FindClose(h);
  352. ULARGE_INTEGER i;
  353. i.HighPart = f.nFileSizeHigh;
  354. i.LowPart = f.nFileSizeLow;
  355. return i.QuadPart;
  356. }
  357. static bool copySettings(wchar_t * ssrc, wchar_t * sdest)
  358. {
  359. FILE * src, * dest;
  360. src=_wfopen(ssrc,L"rt");
  361. if(!src) return false;
  362. dest=_wfopen(sdest,L"wt");
  363. if(!dest)
  364. {
  365. fclose(src); return false;
  366. }
  367. wchar_t buf[1024]=L"";
  368. bool insection=false;
  369. while(fgetws(buf,1024,src))
  370. {
  371. if(buf[0]==L'[' && wcslen(buf)>1) if(buf[wcslen(buf)-2]==L']') insection=false;
  372. if(wcscmp(&buf[0],L"[ml_pmp]\n")==0) insection=true;
  373. if(insection) fputws(&buf[0],dest);
  374. }
  375. fclose(src);
  376. fclose(dest);
  377. return true;
  378. }
  379. HNAVITEM DeviceView::AddPlaylistNode(int id)
  380. {
  381. NAVINSERTSTRUCT nis = {0};
  382. wchar_t title[256] = {0}, name[128] = {0};
  383. dev->getPlaylistName(id, title , ARRAYSIZE(title));
  384. StringCchPrintf(name, ARRAYSIZE(name), L"ml_pmp_playlist_%d", id);
  385. nis.hParent = treeItem;
  386. nis.hInsertAfter = NCI_LAST;
  387. memset(&nis.item, 0, sizeof(nis.item));
  388. nis.item.cbSize = sizeof(NAVITEM);
  389. nis.item.pszText = title;
  390. nis.item.pszInvariant = name;
  391. nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE;
  392. nis.item.iImage = icon_store.GetPlaylistIcon();
  393. nis.item.iSelectedImage = nis.item.iImage;
  394. nis.item.style = NIS_ALLOWEDIT;
  395. nis.item.styleMask = nis.item.style;
  396. HNAVITEM item = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  397. playlistTreeItems.push_back(item);
  398. return item;
  399. }
  400. int DeviceView::CreatePlaylist(wchar_t * name, bool silent)
  401. {
  402. HNAVITEM item;
  403. int playlistId;
  404. if (NULL == name)
  405. {
  406. int count, slot, length;
  407. wchar_t buffer[512] = {0}, buffer2[ARRAYSIZE(buffer)] = {0};
  408. BOOL foundMatch;
  409. name = WASABI_API_LNGSTRINGW_BUF(IDS_NEW_PLAYLIST, buffer, ARRAYSIZE(buffer));
  410. count = dev->getPlaylistCount();
  411. slot = 1;
  412. length = -1;
  413. do
  414. {
  415. foundMatch = FALSE;
  416. for (int i = 1; i < count; i++)
  417. {
  418. dev->getPlaylistName(i, buffer2, ARRAYSIZE(buffer2));
  419. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, buffer2, -1, name, -1))
  420. {
  421. foundMatch = TRUE;
  422. if(name != buffer)
  423. {
  424. if (FAILED(StringCchCopy(buffer, ARRAYSIZE(buffer), name)))
  425. return -1;
  426. }
  427. if (-1 == length)
  428. {
  429. wchar_t *end;
  430. length = lstrlen(buffer);
  431. end = buffer + length;
  432. if(length > 2 && L')' == *(--end))
  433. {
  434. unsigned short charType;
  435. for(wchar_t *begin = --end; begin != buffer; begin--)
  436. {
  437. if (L'(' == *begin)
  438. {
  439. if (begin > buffer && L' ' == *(--begin))
  440. {
  441. length = (int)(intptr_t)(begin - buffer);
  442. slot = 0;
  443. }
  444. break;
  445. }
  446. else if (FALSE == GetStringTypeW(CT_CTYPE1, begin, 1, &charType) ||
  447. 0 == (C1_DIGIT & charType))
  448. {
  449. break;
  450. }
  451. }
  452. }
  453. }
  454. slot++;
  455. if (1 == slot)
  456. buffer[length] = L'\0';
  457. else if (FAILED(StringCchPrintf(buffer + length, ARRAYSIZE(buffer) - length, L" (%d)", slot)))
  458. return false;
  459. break;
  460. }
  461. }
  462. } while(FALSE != foundMatch);
  463. }
  464. playlistId = dev->newPlaylist(name);
  465. if(playlistId == -1)
  466. return -1; // failed
  467. item = AddPlaylistNode(playlistId);
  468. if (NULL == item)
  469. {
  470. dev->deletePlaylist(playlistId);
  471. return -1;
  472. }
  473. DevicePropertiesChanges();
  474. if(!silent)
  475. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  476. return playlistId;
  477. }
  478. void DeviceView::RenamePlaylist(int playlistId)
  479. {
  480. HNAVITEM item;
  481. item = NULL;
  482. if (0 == playlistId)
  483. {
  484. if (0 != dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0))
  485. item = treeItem;
  486. }
  487. else
  488. {
  489. if (playlistId > 0 && playlistId <= (int)playlistTreeItems.size())
  490. item = playlistTreeItems[playlistId - 1];
  491. }
  492. if (NULL != item)
  493. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  494. }
  495. size_t DeviceView::GetPlaylistName(wchar_t *buffer, size_t bufferSize, int playlistId,
  496. const wchar_t *defaultName, BOOL quoteSpaces)
  497. {
  498. size_t length;
  499. if (NULL == buffer || 0 == bufferSize)
  500. return 0;
  501. buffer[0] = L'\0';
  502. if (NULL != dev)
  503. dev->getPlaylistName(playlistId, buffer, bufferSize);
  504. if (FAILED(StringCchLength(buffer, bufferSize, &length)))
  505. return 0;
  506. if (0 == length)
  507. {
  508. if (NULL != defaultName)
  509. {
  510. if (FALSE != IS_INTRESOURCE(defaultName))
  511. WASABI_API_LNGSTRINGW_BUF((int)(intptr_t)defaultName, buffer, bufferSize);
  512. else
  513. {
  514. if (FAILED(StringCchCopy(buffer, bufferSize, defaultName)))
  515. return 0;
  516. }
  517. if (FAILED(StringCchLength(buffer, bufferSize, &length)))
  518. return 0;
  519. }
  520. }
  521. else
  522. {
  523. if (FALSE != quoteSpaces &&
  524. (L' ' == buffer[0] || L' ' == buffer[length-1]) &&
  525. (bufferSize - length) > 2)
  526. {
  527. memmove(buffer + 1, buffer, sizeof(wchar_t) * length);
  528. buffer[0] = L'\"';
  529. buffer[length++] = L'\"';
  530. buffer[length] = L'\0';
  531. }
  532. }
  533. return length;
  534. }
  535. bool DeviceView::DeletePlaylist(int playlistId, bool deleteFiles, bool verbal)
  536. {
  537. int index;
  538. C_ItemList delList;
  539. if(playlistId < 1)
  540. return false;
  541. index = playlistId - 1;
  542. if (false != deleteFiles)
  543. {
  544. int length;
  545. length = dev->getPlaylistLength(playlistId);
  546. for(int i = 0; i < length; i++)
  547. {
  548. delList.Add((void*)dev->getPlaylistTrack(playlistId, i));
  549. }
  550. }
  551. if (false != verbal)
  552. {
  553. wchar_t message[1024] = {0}, title[1024] = {0}, playlistName[256] = {0}, deviceName[256] = {0};
  554. GetPlaylistName(playlistName, ARRAYSIZE(playlistName), playlistId, NULL, FALSE);
  555. GetPlaylistName(deviceName, ARRAYSIZE(deviceName), 0, MAKEINTRESOURCE(IDS_DEVICE_LOWERCASE), TRUE);
  556. if (0 != delList.GetSize())
  557. {
  558. WASABI_API_LNGSTRINGW_BUF(IDS_PHYSICALLY_REMOVE_X_TRACKS, title, ARRAYSIZE(title));
  559. StringCchPrintf(message, ARRAYSIZE(message), title, delList.GetSize(), playlistName);
  560. WASABI_API_LNGSTRINGW_BUF(IDS_DELETE_PLAYLIST_TITLE, title, ARRAYSIZE(title));
  561. }
  562. else
  563. {
  564. WASABI_API_LNGSTRINGW_BUF(IDS_DELETE_PLAYLIST, title, ARRAYSIZE(title));
  565. StringCchPrintf(message, ARRAYSIZE(message), title, playlistName, deviceName);
  566. WASABI_API_LNGSTRINGW_BUF(IDS_DELETE_PLAYLIST_TITLE, title, ARRAYSIZE(title));
  567. }
  568. if(IDYES != MessageBox(plugin.hwndLibraryParent, message, title,
  569. MB_YESNO | MB_ICONWARNING))
  570. {
  571. return false;
  572. }
  573. }
  574. if (0 != delList.GetSize())
  575. {
  576. int result;
  577. result = DeleteTracks(&delList, CENTER_OVER_ML_VIEW);
  578. if (IDABORT == result) /* user abort */
  579. return false;
  580. if (IDOK != result) /* error */
  581. {
  582. }
  583. }
  584. HNAVITEM item = playlistTreeItems[index];
  585. playlistTreeItems.erase(playlistTreeItems.begin() + index);
  586. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, item);
  587. dev->deletePlaylist(playlistId);
  588. DevicePropertiesChanges();
  589. return true;
  590. }
  591. bool DeviceView::GetTransferFromMlSupported(int dataType)
  592. {
  593. switch(dataType)
  594. {
  595. case ML_TYPE_ITEMRECORDLISTW:
  596. case ML_TYPE_ITEMRECORDLIST:
  597. case ML_TYPE_PLAYLIST:
  598. case ML_TYPE_PLAYLISTS:
  599. case ML_TYPE_FILENAMES:
  600. case ML_TYPE_FILENAMESW:
  601. return true;
  602. }
  603. return false;
  604. }
  605. intptr_t DeviceView::TransferFromML(int type, void* data, int unsupportedReturn, int supportedReturn, int playlist)
  606. {
  607. int r;
  608. if (AGAVE_API_STATS)
  609. {
  610. wchar_t device_name[128] = {0};
  611. if(dev->extraActions(DEVICE_GET_MODEL, (intptr_t)device_name, 128, 0) == 1 && device_name[0])
  612. {
  613. AGAVE_API_STATS->SetString("pmp", device_name);
  614. }
  615. }
  616. if(type == ML_TYPE_ITEMRECORDLISTW)
  617. {
  618. r=AddItemListToTransferQueue((itemRecordListW*)data,playlist);
  619. }
  620. else if(type == ML_TYPE_ITEMRECORDLIST)
  621. {
  622. itemRecordListW list= {0};
  623. convertRecordList(&list,(itemRecordList*)data);
  624. r=AddItemListToTransferQueue(&list,playlist);
  625. freeRecordList(&list);
  626. }
  627. else if(type == ML_TYPE_FILENAMES)
  628. {
  629. C_ItemList fileList;
  630. char * filenames = (char *)data;
  631. while(filenames && *filenames)
  632. {
  633. fileList.Add(filenames);
  634. filenames+=strlen(filenames)+1;
  635. }
  636. r=AddFileListToTransferQueue((char**)fileList.GetAll(),fileList.GetSize(),playlist);
  637. }
  638. else if(type == ML_TYPE_FILENAMESW)
  639. {
  640. C_ItemList fileList;
  641. wchar_t * filenames = (wchar_t *)data;
  642. while(filenames && *filenames)
  643. {
  644. fileList.Add(filenames);
  645. filenames+=wcslen(filenames)+1;
  646. }
  647. r=AddFileListToTransferQueue((wchar_t**)fileList.GetAll(),fileList.GetSize(),playlist);
  648. }
  649. else if(type == ML_TYPE_PLAYLIST)
  650. {
  651. mlPlaylist * pl = (mlPlaylist *)data;
  652. TransferPlaylist((wchar_t*)pl->filename,(wchar_t*)pl->title);
  653. r=0;
  654. }
  655. else if(type == ML_TYPE_PLAYLISTS)
  656. {
  657. mlPlaylist **playlists = (mlPlaylist **)data;
  658. while(playlists && *playlists)
  659. {
  660. mlPlaylist *pl = *playlists;
  661. TransferPlaylist((wchar_t*)pl->filename,(wchar_t*)pl->title);
  662. playlists++;
  663. }
  664. r=0;
  665. }
  666. else return unsupportedReturn;
  667. wchar_t errStr[32] = {0};
  668. if(r==-1)
  669. MessageBox(plugin.hwndLibraryParent,
  670. WASABI_API_LNGSTRINGW(IDS_DEVICE_OUT_OF_SPACE),
  671. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,errStr,32),0);
  672. else if(r==-2)
  673. MessageBox(plugin.hwndLibraryParent,
  674. WASABI_API_LNGSTRINGW(IDS_INCOMPATABLE_FORMAT_NO_TX),
  675. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,errStr,32),0);
  676. return supportedReturn;
  677. }
  678. class ItemListRefLoader : public ifc_playlistloadercallback
  679. {
  680. public:
  681. ItemListRefLoader(C_ItemList &itemList, C_ItemList &playlistItemList) : fileList(itemList), playlistList(playlistItemList)
  682. {
  683. }
  684. void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
  685. {
  686. if(playlistManager->CanLoad(filename))
  687. playlistList.Add(_wcsdup(filename));
  688. else
  689. fileList.Add(_wcsdup(filename));
  690. }
  691. C_ItemList &fileList, &playlistList;
  692. protected:
  693. RECVS_DISPATCH;
  694. };
  695. #define CBCLASS ItemListRefLoader
  696. START_DISPATCH;
  697. VCB( IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile )
  698. END_DISPATCH;
  699. #undef CBCLASS
  700. class PlaylistDirectoryCallback : public ifc_playlistdirectorycallback
  701. {
  702. public:
  703. PlaylistDirectoryCallback(const wchar_t *_extlist) : extlist(_extlist)
  704. {
  705. }
  706. bool ShouldRecurse(const wchar_t *path)
  707. {
  708. // TODO: check for recursion?
  709. return true;
  710. }
  711. bool ShouldLoad(const wchar_t *filename)
  712. {
  713. if(playlistManager->CanLoad(filename))
  714. return true;
  715. const wchar_t *ext = PathFindExtensionW(filename);
  716. if(!*ext)
  717. return false;
  718. ext++;
  719. const wchar_t *a = extlist;
  720. while(a && *a)
  721. {
  722. if(!_wcsicmp(a, ext))
  723. return true;
  724. a += wcslen(a) + 1;
  725. }
  726. return false;
  727. }
  728. const wchar_t *extlist;
  729. protected:
  730. RECVS_DISPATCH;
  731. };
  732. #define CBCLASS PlaylistDirectoryCallback
  733. START_DISPATCH;
  734. CB(IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDRECURSE, ShouldRecurse)
  735. CB(IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDLOAD, ShouldLoad)
  736. END_DISPATCH;
  737. #undef CBCLASS
  738. intptr_t DeviceView::TransferFromDrop(HDROP hDrop, int playlist)
  739. {
  740. // benski> ugh. memory allocation hell. oh well
  741. C_ItemList fileList;
  742. C_ItemList playlistList;
  743. const wchar_t *extListW = (const wchar_t *)SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_EXTLISTW);
  744. PlaylistDirectoryCallback dirCB(extListW);
  745. wchar_t temp[2048] = {0};
  746. int y = DragQueryFileW(hDrop, 0xffffffff, temp, 2048);
  747. for(int x = 0; x < y; x ++)
  748. {
  749. DragQueryFileW(hDrop, x, temp, 2048);
  750. // see if it's a directory
  751. bool isDir=false;
  752. if(!PathIsURLW(temp) && !PathIsNetworkPathW(temp))
  753. {
  754. HANDLE h;
  755. WIN32_FIND_DATAW d;
  756. h = FindFirstFileW(temp, &d);
  757. if(h != INVALID_HANDLE_VALUE)
  758. {
  759. FindClose(h);
  760. if(d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  761. {
  762. ItemListRefLoader fileListCB(fileList, playlistList);
  763. playlistManager->LoadDirectory(temp, &fileListCB, &dirCB);
  764. isDir=true;
  765. }
  766. }
  767. }
  768. if(!isDir)
  769. {
  770. if(playlistManager->CanLoad(temp))
  771. playlistList.Add(_wcsdup(temp));
  772. else
  773. fileList.Add(_wcsdup(temp));
  774. }
  775. }
  776. int r=0, r2=0;
  777. if(fileList.GetSize())
  778. r=AddFileListToTransferQueue((wchar_t**)fileList.GetAll(),fileList.GetSize(),playlist);
  779. #if 0
  780. if(playlistList.GetSize())
  781. r2=AddFileListToTransferQueue((wchar_t**)playlistList.GetAll(), playlistList.GetSize(),1/*playlists*/);
  782. #endif
  783. wchar_t errStr[32] = {0};
  784. if(r==-1 || r2==-1)
  785. MessageBox(plugin.hwndLibraryParent,
  786. WASABI_API_LNGSTRINGW(IDS_DEVICE_OUT_OF_SPACE),
  787. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,errStr,32),0);
  788. if(r==-2 || r2 == -2)
  789. MessageBox(plugin.hwndLibraryParent,
  790. WASABI_API_LNGSTRINGW(IDS_INCOMPATABLE_FORMAT_NO_TX),
  791. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,errStr,32),0);
  792. // benski> my CS301 professor would be proud!
  793. fileList.for_all(free);
  794. playlistList.for_all(free);
  795. GlobalFree((HGLOBAL)extListW);
  796. return 0;
  797. }
  798. HWND DeviceView::CreateView(HWND parent)
  799. {
  800. currentViewedDevice=this;
  801. currentViewedPlaylist=0;
  802. if (currentViewedDevice->config->ReadInt(L"media_numfilters", 2) == 1)
  803. return WASABI_API_CREATEDIALOGPARAMW((isCloudDevice ? IDD_VIEW_CLOUD_SIMPLE : IDD_VIEW_PMP_VIDEO),
  804. parent, pmp_video_dlgproc, (currentViewedDevice->isCloudDevice ? 1 : 2));
  805. else
  806. return WASABI_API_CREATEDIALOGPARAMW((isCloudDevice ? IDD_VIEW_CLOUD_ARTISTALBUM : IDD_VIEW_PMP_ARTISTALBUM),
  807. parent, pmp_artistalbum_dlgproc, 0);
  808. }
  809. BOOL DeviceView::DisplayDeviceContextMenu(HNAVITEM item, HWND hostWindow, POINT pt)
  810. {
  811. HMENU menu = GetSubMenu(m_context_menus,3);
  812. if (NULL == menu)
  813. return FALSE;
  814. if(dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0))
  815. AppendMenu(menu,0,ID_TREEPLAYLIST_RENAMEPLAYLIST,WASABI_API_LNGSTRINGW(IDS_RENAME_DEVICE));
  816. int r = Menu_TrackSkinnedPopup(menu, TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,
  817. pt.x, pt.y, plugin.hwndLibraryParent, NULL);
  818. if(dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0))
  819. DeleteMenu(menu,ID_TREEPLAYLIST_RENAMEPLAYLIST,MF_BYCOMMAND);
  820. switch(r)
  821. {
  822. case ID_TREEDEVICE_NEWPLAYLIST:
  823. CreatePlaylist();
  824. break;
  825. case ID_TREEDEVICE_EJECTDEVICE:
  826. Eject();
  827. break;
  828. case ID_TREEPLAYLIST_RENAMEPLAYLIST:
  829. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  830. //RenamePlaylist(0);
  831. break;
  832. }
  833. return TRUE;
  834. }
  835. BOOL DeviceView::DisplayPlaylistContextMenu(int playlistId, HNAVITEM item, HWND hostWindow, POINT pt)
  836. {
  837. HMENU menu = GetSubMenu(m_context_menus,4);
  838. if (NULL == menu)
  839. return FALSE;
  840. EnableMenuItem(menu,ID_TREEPLAYLIST_COPYPLAYLISTTOLOCALMEDIA,
  841. MF_BYCOMMAND | (dev->copyToHardDriveSupported()? MF_ENABLED : (MF_DISABLED | MF_GRAYED)));
  842. int r = Menu_TrackSkinnedPopup(menu,TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,
  843. pt.x, pt.y, plugin.hwndLibraryParent, NULL);
  844. switch(r)
  845. {
  846. case ID_TREEPLAYLIST_RENAMEPLAYLIST:
  847. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  848. break;
  849. case ID_TREEPLAYLIST_REMOVEPLAYLISTANDFILES:
  850. DeletePlaylist(playlistId, true, true);
  851. break;
  852. case ID_TREEPLAYLIST_REMOVEPLAYLIST:
  853. DeletePlaylist(playlistId, false, true);
  854. break;
  855. case ID_TREEPLAYLIST_COPYPLAYLISTTOLOCALMEDIA:
  856. CopyPlaylistToLibrary(playlistId);
  857. break;
  858. }
  859. return TRUE;
  860. }
  861. static HNAVITEM Navigation_GetItemFromMessage(INT msg, INT_PTR param)
  862. {
  863. return (msg < ML_MSG_NAVIGATION_FIRST) ?
  864. MLNavCtrl_FindItemById(plugin.hwndLibraryParent, param) :
  865. (HNAVITEM)param;
  866. }
  867. BOOL DeviceView::Navigation_IsPlaylistItem(HNAVITEM item, int *playlistId)
  868. {
  869. for(size_t i=0; i < playlistTreeItems.size(); i++)
  870. {
  871. if(item == playlistTreeItems[i])
  872. {
  873. if (NULL != playlistId)
  874. *playlistId = (i + 1);
  875. return TRUE;
  876. }
  877. }
  878. return FALSE;
  879. }
  880. HWND DeviceView::Navigation_CreateViewCb(HNAVITEM item, HWND parentWindow)
  881. {
  882. if(item == treeItem)
  883. {
  884. if (FALSE != navigationItemCreated)
  885. {
  886. currentViewedDevice = this;
  887. currentViewedPlaylist = 0;
  888. return WASABI_API_CREATEDIALOGW((currentViewedDevice->isCloudDevice ? IDD_VIEW_CLOUD_ARTISTALBUM : IDD_VIEW_PMP_ARTISTALBUM), parentWindow, pmp_artistalbum_dlgproc);
  889. }
  890. }
  891. else if (item == queueTreeItem)
  892. {
  893. currentViewedDevice = this;
  894. currentViewedPlaylist = 0;
  895. return WASABI_API_CREATEDIALOGPARAMW((currentViewedDevice->isCloudDevice ? IDD_VIEW_CLOUD_QUEUE : IDD_VIEW_PMP_QUEUE), parentWindow, pmp_queue_dlgproc, (LPARAM)this);
  896. }
  897. else if(item == videoTreeItem)
  898. {
  899. currentViewedDevice = this;
  900. currentViewedPlaylist = 0;
  901. return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_PMP_VIDEO, parentWindow, pmp_video_dlgproc, 0);
  902. }
  903. else
  904. {
  905. if (FALSE != Navigation_IsPlaylistItem(item, &currentViewedPlaylist))
  906. {
  907. currentViewedDevice = this;
  908. return WASABI_API_CREATEDIALOGW(IDD_VIEW_PMP_PLAYLIST, parentWindow, pmp_playlist_dlgproc);
  909. }
  910. }
  911. return NULL;
  912. }
  913. BOOL DeviceView::Navigation_ShowContextMenuCb(HNAVITEM item, HWND hostWindow, POINT pt)
  914. {
  915. if (item == treeItem)
  916. {
  917. if (FALSE != navigationItemCreated)
  918. return DisplayDeviceContextMenu(item, hostWindow, pt);
  919. }
  920. else
  921. {
  922. int playlistId;
  923. if (FALSE != Navigation_IsPlaylistItem(item, &playlistId))
  924. return DisplayPlaylistContextMenu(playlistId, item, hostWindow, pt);
  925. }
  926. return FALSE;
  927. }
  928. BOOL DeviceView::Navigation_ClickCb(HNAVITEM item, int actionType, HWND hostWindow)
  929. {
  930. int playlistId;
  931. switch(actionType)
  932. {
  933. case ML_ACTION_DBLCLICK:
  934. case ML_ACTION_ENTER:
  935. if (FALSE != Navigation_IsPlaylistItem(item, &playlistId))
  936. {
  937. PlayPlaylist(playlistId, false, true, hostWindow);
  938. return TRUE;
  939. }
  940. break;
  941. }
  942. return FALSE;
  943. }
  944. int DeviceView::Navigation_DropTargetCb(HNAVITEM item, unsigned int dataType, void *data)
  945. {
  946. if (item == treeItem)
  947. {
  948. if (FALSE != navigationItemCreated)
  949. {
  950. if(NULL == data)
  951. return (false != GetTransferFromMlSupported(dataType)) ? 1 : -1;
  952. return TransferFromML(dataType, data, -1, 1);
  953. }
  954. }
  955. else
  956. {
  957. int playlistId;
  958. if (FALSE != Navigation_IsPlaylistItem(item, &playlistId))
  959. {
  960. if(NULL == data)
  961. return (false != GetTransferFromMlSupported(dataType)) ? 1 : -1;
  962. return TransferFromML(dataType, data, -1, 1, playlistId);
  963. }
  964. }
  965. return FALSE;
  966. }
  967. BOOL DeviceView::Navigation_TitleEditBeginCb(HNAVITEM item)
  968. {
  969. if (item == treeItem)
  970. {
  971. if (FALSE != navigationItemCreated &&
  972. FALSE == dev->extraActions(DEVICE_CAN_RENAME_DEVICE,0,0,0))
  973. {
  974. return TRUE;
  975. }
  976. }
  977. return FALSE;
  978. }
  979. BOOL DeviceView::Navigation_TitleEditEndCb(HNAVITEM item, const wchar_t *title)
  980. {
  981. int playlistId = 0;
  982. wchar_t buffer[512] = {0};
  983. if (item == treeItem)
  984. {
  985. if (FALSE == navigationItemCreated)
  986. return FALSE;
  987. playlistId = 0;
  988. }
  989. else
  990. {
  991. if (FALSE == Navigation_IsPlaylistItem(item, &playlistId))
  992. return FALSE;
  993. }
  994. if (NULL == title)
  995. return TRUE;
  996. buffer[0] = L'\0';
  997. dev->getPlaylistName(playlistId, buffer, ARRAYSIZE(buffer));
  998. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, buffer, -1, title, -1))
  999. return TRUE;
  1000. dev->setPlaylistName(playlistId, title);
  1001. DevicePropertiesChanges();
  1002. buffer[0] = L'\0';
  1003. dev->getPlaylistName(playlistId, buffer, ARRAYSIZE(buffer));
  1004. if (0 == playlistId)
  1005. {
  1006. free(devPrefsPage.name);
  1007. devPrefsPage.name = _wcsdup(buffer);
  1008. UpdateDevicesListView(false);
  1009. OnNameChanged(buffer);
  1010. }
  1011. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, buffer, -1, title, -1))
  1012. return TRUE;
  1013. NAVITEM itemInfo;
  1014. itemInfo.cbSize = sizeof(itemInfo);
  1015. itemInfo.pszText = buffer;
  1016. itemInfo.hItem = item;
  1017. itemInfo.mask = NIMF_TEXT;
  1018. MLNavItem_SetInfo(plugin.hwndLibraryParent, &itemInfo);
  1019. return FALSE;
  1020. }
  1021. BOOL DeviceView::Navigation_KeyDownCb(HNAVITEM item, NMTVKEYDOWN *keyData, HWND hwnd)
  1022. {
  1023. int playlistId;
  1024. if (item == treeItem)
  1025. {
  1026. if (FALSE == navigationItemCreated)
  1027. return FALSE;
  1028. switch(keyData->wVKey)
  1029. {
  1030. case VK_F2:
  1031. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  1032. break;
  1033. }
  1034. return TRUE;
  1035. }
  1036. if (FALSE != Navigation_IsPlaylistItem(item, &playlistId))
  1037. {
  1038. switch(keyData->wVKey)
  1039. {
  1040. case VK_F2:
  1041. MLNavItem_EditTitle(plugin.hwndLibraryParent, item);
  1042. break;
  1043. case VK_DELETE:
  1044. DeletePlaylist(playlistId, false, true);
  1045. break;
  1046. }
  1047. return TRUE;
  1048. }
  1049. return FALSE;
  1050. }
  1051. intptr_t DeviceView::MessageProc(int message_type, intptr_t param1, intptr_t param2, intptr_t param3)
  1052. {
  1053. if(message_type >= ML_MSG_TREE_BEGIN && message_type <= ML_MSG_TREE_END)
  1054. {
  1055. HNAVITEM item;
  1056. switch(message_type)
  1057. {
  1058. case ML_MSG_TREE_ONCREATEVIEW:
  1059. item = Navigation_GetItemFromMessage(message_type, param1);
  1060. return (INT_PTR)Navigation_CreateViewCb(item, (HWND)param2);
  1061. case ML_MSG_NAVIGATION_CONTEXTMENU:
  1062. {
  1063. POINT pt;
  1064. POINTSTOPOINT(pt, MAKEPOINTS(param3));
  1065. item = Navigation_GetItemFromMessage(message_type, param1);
  1066. return (INT_PTR)Navigation_ShowContextMenuCb(item, (HWND)param2, pt);
  1067. }
  1068. case ML_MSG_TREE_ONCLICK:
  1069. item = Navigation_GetItemFromMessage(message_type, param1);
  1070. return (INT_PTR)Navigation_ClickCb(item, (int)param2, (HWND)param3);
  1071. case ML_MSG_TREE_ONDROPTARGET:
  1072. item = Navigation_GetItemFromMessage(message_type, param1);
  1073. return (INT_PTR)Navigation_DropTargetCb(item, (unsigned int)param2, (void*)param3);
  1074. case ML_MSG_NAVIGATION_ONBEGINTITLEEDIT:
  1075. item = Navigation_GetItemFromMessage(message_type, param1);
  1076. return (INT_PTR)Navigation_TitleEditBeginCb(item);
  1077. case ML_MSG_NAVIGATION_ONENDTITLEEDIT:
  1078. item = Navigation_GetItemFromMessage(message_type, param1);
  1079. return (INT_PTR)Navigation_TitleEditEndCb(item, (const wchar_t*)param2);
  1080. case ML_MSG_TREE_ONKEYDOWN:
  1081. item = Navigation_GetItemFromMessage(message_type, param1);
  1082. return (INT_PTR)Navigation_KeyDownCb(item, (NMTVKEYDOWN*)param2, (HWND)param3);
  1083. }
  1084. }
  1085. else if(message_type == ML_MSG_ONSENDTOBUILD)
  1086. {
  1087. if (!gen_mlconfig->ReadInt(L"pmp_send_to", DEFAULT_PMP_SEND_TO))
  1088. {
  1089. if (param1 == ML_TYPE_ITEMRECORDLIST || param1 == ML_TYPE_ITEMRECORDLISTW ||
  1090. param1 == ML_TYPE_FILENAMES || param1 == ML_TYPE_FILENAMESW ||
  1091. param1 == ML_TYPE_PLAYLIST || param1 == ML_TYPE_PLAYLISTS)
  1092. {
  1093. if (dev->extraActions(DEVICE_SENDTO_UNSUPPORTED, 0, 0, 0) == 0)
  1094. {
  1095. wchar_t buffer[128] = {0};
  1096. dev->getPlaylistName(0, buffer, 128);
  1097. mediaLibrary.AddToSendTo(buffer, param2, reinterpret_cast<INT_PTR>(this));
  1098. }
  1099. }
  1100. }
  1101. }
  1102. else if(message_type == ML_MSG_ONSENDTOSELECT && param2 && param3 == (intptr_t)this)
  1103. {
  1104. // TODO!!!
  1105. // if we get a playlist or playlist list and we can match it to a cloud device then
  1106. // we check for 'hss' and if so then process as a cloud playlist else do as before
  1107. if (this->isCloudDevice && (param1 == ML_TYPE_PLAYLIST || param1 == ML_TYPE_PLAYLISTS))
  1108. {
  1109. char name[128] = {0};
  1110. if (dev->extraActions(DEVICE_GET_UNIQUE_ID, (intptr_t)name, sizeof(name), 0))
  1111. {
  1112. if (!strcmp(name, "hss"/*HSS_CLIENT*/))
  1113. {
  1114. if(param1 == ML_TYPE_PLAYLIST)
  1115. {
  1116. mlPlaylist * pl = (mlPlaylist *)param2;
  1117. TransferAddCloudPlaylist((wchar_t*)pl->filename, (wchar_t*)pl->title);
  1118. }
  1119. else if(param1 == ML_TYPE_PLAYLISTS)
  1120. {
  1121. mlPlaylist **playlists = (mlPlaylist **)param2;
  1122. while(playlists && *playlists)
  1123. {
  1124. mlPlaylist *pl = *playlists;
  1125. TransferAddCloudPlaylist((wchar_t*)pl->filename, (wchar_t*)pl->title);
  1126. playlists++;
  1127. }
  1128. }
  1129. return 1;
  1130. }
  1131. }
  1132. }
  1133. UpdateActivityState();
  1134. return TransferFromML(param1,(void*)param2,0,1);
  1135. }
  1136. return 0;
  1137. }
  1138. void DeviceView::DevicePropertiesChanges()
  1139. {
  1140. commitNeeded=true;
  1141. SetEvent(transferContext.notifier);
  1142. }
  1143. void DeviceView::Eject()
  1144. {
  1145. LinkedQueue * txQueue = getTransferQueue(this);
  1146. if(txQueue && txQueue->GetSize() == 0)
  1147. {
  1148. dev->Eject();
  1149. }
  1150. else
  1151. {
  1152. wchar_t titleStr[32] = {0};
  1153. MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_SYNC_IS_IN_PROGRESS),
  1154. WASABI_API_LNGSTRINGW_BUF(IDS_CANNOT_EJECT,titleStr,32),0);
  1155. }
  1156. }
  1157. Device * deleteTrackDev;
  1158. C_ItemList * deleteTracks;
  1159. extern HWND hwndMediaView;
  1160. static INT_PTR CALLBACK pmp_delete_progress_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
  1161. {
  1162. static int i;
  1163. static songid_t s;
  1164. switch(uMsg)
  1165. {
  1166. case WM_INITDIALOG:
  1167. i=0;
  1168. s=0;
  1169. SetWindowText(hwndDlg,WASABI_API_LNGSTRINGW((!currentViewedDevice || currentViewedDevice && !currentViewedDevice->isCloudDevice ? IDS_DELETING_TRACKS : IDS_REMOVING_TRACKS)));
  1170. SendDlgItemMessage(hwndDlg,IDC_PROGRESS,PBM_SETRANGE,0,MAKELPARAM(0, (!deleteTracks ? 0 : deleteTracks->GetSize())));
  1171. SetTimer(hwndDlg,0,5,NULL);
  1172. if (FALSE != CenterWindow(hwndDlg, (HWND)lParam))
  1173. SendMessage(hwndDlg, DM_REPOSITION, 0, 0L);
  1174. break;
  1175. case WM_TIMER:
  1176. if(wParam == 1)
  1177. {
  1178. KillTimer(hwndDlg,wParam);
  1179. SendDlgItemMessage(hwndDlg,IDC_PROGRESS,PBM_SETPOS,i,0);
  1180. if(i < deleteTracks->GetSize())
  1181. {
  1182. songid_t s2 = (songid_t)deleteTracks->Get(i++);
  1183. if(s != s2)
  1184. {
  1185. if(hwndMediaView) SendMessage(hwndMediaView,WM_USER+1,(WPARAM)s2,0);
  1186. deleteTrackDev->deleteTrack(s2);
  1187. s=s2;
  1188. }
  1189. SetTimer(hwndDlg,1,5,NULL);
  1190. }
  1191. else EndDialog(hwndDlg, IDOK);
  1192. }
  1193. else if(wParam == 0)
  1194. {
  1195. KillTimer(hwndDlg,0);
  1196. int s = deleteTracks->GetSize();
  1197. for(int i=0; i<s; i++)
  1198. {
  1199. void * p = deleteTracks->Get(i);
  1200. for(int j=i+1; j<s; j++)
  1201. {
  1202. if(p == deleteTracks->Get(j))
  1203. {
  1204. deleteTracks->Del(j--); s--;
  1205. }
  1206. }
  1207. }
  1208. SetTimer(hwndDlg,1,5,NULL);
  1209. }
  1210. break;
  1211. case WM_COMMAND:
  1212. if(LOWORD(wParam) == IDC_ABORT)
  1213. EndDialog(hwndDlg, IDABORT);
  1214. break;
  1215. }
  1216. return 0;
  1217. }
  1218. int DeviceView::DeleteTracks(C_ItemList * tracks, HWND centerWindow)
  1219. {
  1220. LinkedQueue * txQueue = getTransferQueue(this);
  1221. if(txQueue && txQueue->GetSize() > 0)
  1222. {
  1223. wchar_t sorry[32] = {0};
  1224. MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_CANNOT_REMOVE_TRACKS_WHILE_TRANSFERING),
  1225. WASABI_API_LNGSTRINGW_BUF(IDS_SORRY,sorry,32),0);
  1226. return -1;
  1227. }
  1228. if (dev && tracks)
  1229. {
  1230. deleteTrackDev = dev;
  1231. deleteTracks = tracks;
  1232. return WASABI_API_DIALOGBOXPARAMW(IDD_PROGRESS, plugin.hwndLibraryParent, pmp_delete_progress_dlgproc, (LPARAM)centerWindow);
  1233. }
  1234. return IDABORT;
  1235. }
  1236. int DeviceView::AddFileListToTransferQueue(char ** files, int num, int playlist)
  1237. {
  1238. wchar_t ** filesW = (wchar_t**)calloc(num, sizeof(wchar_t*));
  1239. for(int i=0; i<num; i++)
  1240. {
  1241. filesW[i] = AutoWideDup(files[i]);
  1242. }
  1243. int r = AddFileListToTransferQueue(filesW,num,playlist);
  1244. for(int i=0; i<num; i++)
  1245. {
  1246. free(filesW[i]);
  1247. }
  1248. free(filesW);
  1249. return r;
  1250. }
  1251. int DeviceView::AddFileListToTransferQueue(wchar_t ** files, int num, int playlist)
  1252. {
  1253. C_ItemList * irs = fileListToItemRecords(files,num, CENTER_OVER_ML_VIEW);
  1254. int r = AddItemListToTransferQueue(irs,playlist);
  1255. for(int i=0; i < irs->GetSize(); i++)
  1256. {
  1257. itemRecordW * it = (itemRecordW *)irs->Get(i);
  1258. freeRecord(it);
  1259. free(it);
  1260. }
  1261. delete irs;
  1262. return r;
  1263. }
  1264. void TransfersListPushPopItem(CopyInst * item, DeviceView *view);
  1265. int DeviceView::AddItemListToTransferQueue(C_ItemList * items, int playlist)
  1266. {
  1267. if(playlist == 0)
  1268. {
  1269. if (!isCloudDevice)
  1270. {
  1271. int r=0;
  1272. C_ItemList toSend, haveSent;
  1273. ProcessDatabaseDifferences(dev,items,&haveSent,&toSend,NULL,NULL);
  1274. LinkedQueue * txQueue = getTransferQueue(this);
  1275. if (txQueue)
  1276. {
  1277. txQueue->lock();
  1278. for(int i = 0; i < toSend.GetSize(); i++)
  1279. {
  1280. if((r = this->AddTrackToTransferQueue(this, (itemRecordW*)toSend.Get(i), true)) == -1) break;
  1281. }
  1282. txQueue->unlock();
  1283. }
  1284. return r;
  1285. }
  1286. else
  1287. {
  1288. int r=0;
  1289. LinkedQueue * txQueue = getTransferQueue(this);
  1290. if (txQueue)
  1291. {
  1292. txQueue->lock();
  1293. for(int i = 0; i < items->GetSize(); i++)
  1294. {
  1295. if((r = this->AddTrackToTransferQueue(this, (itemRecordW*)items->Get(i), true)) == -1) break;
  1296. }
  1297. txQueue->unlock();
  1298. }
  1299. return r;
  1300. }
  1301. }
  1302. else
  1303. {
  1304. return TransferTracksToPlaylist(items,playlist);
  1305. }
  1306. }
  1307. int DeviceView::AddItemListToTransferQueue(itemRecordListW * items, int playlist)
  1308. {
  1309. if(playlist == 0)
  1310. {
  1311. if (!isCloudDevice)
  1312. {
  1313. int r=0;
  1314. C_ItemList toSend;
  1315. ProcessDatabaseDifferences(dev,items,NULL,&toSend,NULL,NULL);
  1316. LinkedQueue * txQueue = getTransferQueue(this);
  1317. if (txQueue)
  1318. {
  1319. txQueue->lock();
  1320. for (int i = 0; i < toSend.GetSize(); i++)
  1321. if((r = this->AddTrackToTransferQueue(this, (itemRecordW*)toSend.Get(i), true)) == -1) break;
  1322. txQueue->unlock();
  1323. }
  1324. return r;
  1325. }
  1326. else
  1327. {
  1328. int r=0;
  1329. LinkedQueue * txQueue = getTransferQueue(this);
  1330. if (txQueue)
  1331. {
  1332. txQueue->lock();
  1333. for (int i = 0; i < items->Size; i++)
  1334. if((r = this->AddTrackToTransferQueue(this, &items->Items[i], true)) == -1) break;
  1335. txQueue->unlock();
  1336. }
  1337. return r;
  1338. }
  1339. }
  1340. else
  1341. {
  1342. C_ItemList itemRecords;
  1343. for (int i = 0; i < items->Size; i++) itemRecords.Add(&items->Items[i]);
  1344. return TransferTracksToPlaylist(&itemRecords,playlist);
  1345. }
  1346. }
  1347. class ItemListLoader : public ifc_playlistloadercallback
  1348. {
  1349. public:
  1350. void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
  1351. {
  1352. fileList.Add(_wcsdup(filename));
  1353. }
  1354. void FreeAll()
  1355. {
  1356. fileList.for_all(free);
  1357. }
  1358. C_ItemList fileList;
  1359. protected:
  1360. RECVS_DISPATCH;
  1361. };
  1362. #define CBCLASS ItemListLoader
  1363. START_DISPATCH;
  1364. VCB( IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile )
  1365. END_DISPATCH;
  1366. #undef CBCLASS
  1367. void DeviceView::TransferPlaylist(wchar_t * file, wchar_t * name0)
  1368. {
  1369. // first sort the name out
  1370. if(!file) return;
  1371. wchar_t name[256] = {0};
  1372. if(!name0)
  1373. {
  1374. wchar_t * s = wcsrchr(file,L'\\');
  1375. if(!s) s = wcsrchr(file,L'/');
  1376. if(!s) s = file;
  1377. else s++;
  1378. if(wcslen(s) >= 255) s[255]=0;
  1379. StringCchCopy(name,256, s);
  1380. wchar_t * e = wcsrchr(name,L'.');
  1381. if(e) *e=0;
  1382. }
  1383. else lstrcpyn(name,name0,255);
  1384. name[255]=0;
  1385. // name sorted, parse m3u
  1386. ItemListLoader fileList;
  1387. playlistManager->Load(file, &fileList);
  1388. C_ItemList *itemRecords = fileListToItemRecords(&fileList.fileList, CENTER_OVER_ML_VIEW);
  1389. fileList.FreeAll();
  1390. // now we have a list of itemRecords, lets try and add this playlist to the device!
  1391. int plid = CreatePlaylist(name,true);
  1392. if(plid != -1)
  1393. TransferTracksToPlaylist(itemRecords,plid);
  1394. delete itemRecords;
  1395. }
  1396. int DeviceView::TransferTracksToPlaylist(C_ItemList *itemRecords, int plid)
  1397. {
  1398. wchar_t name[256] = {0};
  1399. dev->getPlaylistName(plid,name,256);
  1400. int i;
  1401. for(i=0; i<itemRecords->GetSize(); i++)
  1402. {
  1403. itemRecordW * ice = (itemRecordW *)itemRecords->Get(i);
  1404. wchar_t num[12] = {0};
  1405. StringCchPrintf(num, 12, L"%x",i);
  1406. setRecordExtendedItem(ice,L"PLN",num);
  1407. }
  1408. C_ItemList irAlreadyOn, siAlreadyOn;
  1409. ProcessDatabaseDifferences(dev,itemRecords,&irAlreadyOn,NULL,&siAlreadyOn,NULL);
  1410. // itemRecords_sort, irAlreadyOn, irTransfer and siAlreadyOn will NOT be in playlist order
  1411. // we must get them into playlist order. In O(n) :/
  1412. int l = itemRecords->GetSize();
  1413. PlaylistAddItem * pl = (PlaylistAddItem*)calloc(l,sizeof(PlaylistAddItem));
  1414. int on=0;
  1415. for(i=0; i < l; i++)
  1416. {
  1417. itemRecordW * ice = (itemRecordW *)itemRecords->Get(i);
  1418. int n;
  1419. swscanf(getRecordExtendedItem(ice,L"PLN"),L"%x",&n);
  1420. if(n >= l)
  1421. {
  1422. continue;
  1423. }
  1424. pl[n].item = ice;
  1425. if(on < irAlreadyOn.GetSize()) if((itemRecordW*)irAlreadyOn.Get(on) == ice) // this track is on the device!
  1426. {
  1427. pl[n].songid = (songid_t)siAlreadyOn.Get(on);
  1428. on++;
  1429. }
  1430. }
  1431. // awesome! pl now contains our playlist in proper order with the "songid" fields set if the track is on the device.
  1432. C_ItemList * directAdd = new C_ItemList;
  1433. int m = 0;
  1434. LinkedQueue * txQueue = getTransferQueue(this);
  1435. if (txQueue)
  1436. {
  1437. PlaylistCopyInst * inst = NULL;
  1438. txQueue->lock();
  1439. for(i=0; i < l; i++)
  1440. {
  1441. if(pl[i].songid)
  1442. {
  1443. directAdd->Add((void*)pl[i].songid);
  1444. }
  1445. else
  1446. {
  1447. int r = dev->trackAddedToTransferQueue(pl[i].item);
  1448. if(r)
  1449. {
  1450. m |= (-r);
  1451. freeRecord(pl[i].item);
  1452. continue;
  1453. }
  1454. if(!inst)
  1455. {
  1456. if(plid != -1) for(int i=0; i<directAdd->GetSize(); i++)
  1457. dev->addTrackToPlaylist(plid,(songid_t)directAdd->Get(i));
  1458. delete directAdd;
  1459. }
  1460. else
  1461. {
  1462. inst->plAddSongs = directAdd;
  1463. AddTrackToTransferQueue(inst);
  1464. }
  1465. directAdd = new C_ItemList;
  1466. inst = new PlaylistCopyInst(this,pl[i].item,name,plid);
  1467. }
  1468. freeRecord(pl[i].item);
  1469. }
  1470. if(inst)
  1471. {
  1472. inst->plAddSongs = directAdd;
  1473. AddTrackToTransferQueue(inst);
  1474. }
  1475. else // NULL inst means no transfers!
  1476. {
  1477. if(plid != -1) for(int i=0; i<directAdd->GetSize(); i++)
  1478. dev->addTrackToPlaylist(plid,(songid_t)directAdd->Get(i));
  1479. delete directAdd;
  1480. }
  1481. txQueue->unlock();
  1482. }
  1483. if (pl) free(pl);
  1484. wchar_t warnStr[32] = {0};
  1485. WASABI_API_LNGSTRINGW_BUF(IDS_WARNING,warnStr,32);
  1486. if(m == 1) MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_NATS_DEVICE_MAYBE_FULL),warnStr,0);
  1487. else if(m == 2) MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_NATS_SOME_OF_INCOMPATABLE_FORMAT),warnStr,0);
  1488. else if(m == 3) MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_NATS_MAYBE_FULL_AND_INCOMPATABLE_FORMAT),warnStr,0);
  1489. return 0;
  1490. }
  1491. void DeviceView::TransferAddCloudPlaylist(wchar_t * file, wchar_t * name0)
  1492. {
  1493. if (!file) return;
  1494. AGAVE_API_PLAYLISTS->Lock();
  1495. for (size_t index = 0; index < AGAVE_API_PLAYLISTS->GetCount(); index++)
  1496. {
  1497. const wchar_t* filename = AGAVE_API_PLAYLISTS->GetFilename(index);
  1498. if (!lstrcmpiW(filename, file))
  1499. {
  1500. int cloud = 1;
  1501. if (AGAVE_API_PLAYLISTS->GetInfo(index, api_playlists_cloud, &cloud, sizeof(cloud)) == API_PLAYLISTS_SUCCESS)
  1502. {
  1503. // not set as a cloud playlist so we need to set and then announce
  1504. if (!cloud)
  1505. {
  1506. cloud = 1;
  1507. AGAVE_API_PLAYLISTS->SetInfo(index, api_playlists_cloud, &cloud, sizeof(cloud));
  1508. AGAVE_API_PLAYLISTS->Flush();
  1509. }
  1510. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  1511. if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
  1512. {
  1513. winampMediaLibraryPlugin *(*gp)();
  1514. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
  1515. if (gp)
  1516. {
  1517. winampMediaLibraryPlugin *mlplugin = gp();
  1518. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  1519. {
  1520. mlplugin->MessageProc(0x406, index, 0, 0);
  1521. }
  1522. }
  1523. }
  1524. PostMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_LIBRARY_PLAYLISTS_REFRESH);
  1525. }
  1526. else
  1527. {
  1528. }
  1529. break;
  1530. }
  1531. }
  1532. AGAVE_API_PLAYLISTS->Unlock();
  1533. }
  1534. extern MLTREEITEMW mainTreeItem;
  1535. HWND hwndToolTips=NULL;
  1536. int DeviceView::AddTrackToTransferQueue(CopyInst * inst)
  1537. {
  1538. LinkedQueue * txQueue = getTransferQueue(this);
  1539. if (txQueue)
  1540. {
  1541. txQueue->Offer(inst);
  1542. if(txQueue->GetSize() == 1)
  1543. SetEvent(transferContext.notifier);
  1544. device_update_map[0] = true;
  1545. device_update_map[inst->dev] = true;
  1546. }
  1547. return 0;
  1548. }
  1549. int DeviceView::AddTrackToTransferQueue(DeviceView * device, itemRecordW * item, bool dupeCheck, bool forceDupe)
  1550. {
  1551. SongCopyInst * inst = new SongCopyInst(device, item);
  1552. if (dupeCheck)
  1553. {
  1554. LinkedQueue * txQueue = getTransferQueue(this);
  1555. if (txQueue)
  1556. {
  1557. txQueue->lock();
  1558. // current queue dupe check
  1559. for(int i = 0; i < txQueue->GetSize(); i++)
  1560. {
  1561. if(((CopyInst *)txQueue->Get(i))->Equals(inst))
  1562. {
  1563. delete inst;
  1564. txQueue->unlock();
  1565. return 0;
  1566. }
  1567. }
  1568. txQueue->unlock();
  1569. }
  1570. }
  1571. if (!forceDupe)
  1572. {
  1573. int r = dev->trackAddedToTransferQueue(&inst->song);
  1574. if (r)
  1575. {
  1576. if (r == 2)
  1577. {
  1578. inst->status = STATUS_DONE;
  1579. inst->res = 2;
  1580. AddTrackToTransferQueue(inst);
  1581. return 0;
  1582. }
  1583. else
  1584. {
  1585. delete inst;
  1586. }
  1587. }
  1588. else AddTrackToTransferQueue(inst);
  1589. return r;
  1590. }
  1591. else
  1592. {
  1593. inst->res = 2;
  1594. AddTrackToTransferQueue(inst);
  1595. return 0;
  1596. }
  1597. }
  1598. class SyncItemListLoader : public ifc_playlistloadercallback
  1599. {
  1600. public:
  1601. void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
  1602. {
  1603. if(pos < len)
  1604. {
  1605. songs[pos].filename = _wcsdup(filename);
  1606. metaToGet->Add(&songs[pos].map);
  1607. songMaps->Add(&songs[pos].pladd);
  1608. }
  1609. pos++;
  1610. }
  1611. int pos,len;
  1612. songMapping * songs;
  1613. C_ItemList * metaToGet, * songMaps;
  1614. protected:
  1615. RECVS_DISPATCH;
  1616. };
  1617. #define CBCLASS SyncItemListLoader
  1618. START_DISPATCH;
  1619. VCB(IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile)
  1620. END_DISPATCH;
  1621. #undef CBCLASS
  1622. typedef struct
  1623. {
  1624. mlPlaylistInfo info;
  1625. songMapping * songs;
  1626. } SyncPlaylist;
  1627. void TransfersListUpdateItem(CopyInst * item);
  1628. void TransfersListUpdateItem(CopyInst * item, DeviceView *view);
  1629. /*
  1630. static int setPlaylistTrack(Device * dev, int pl, int n, int len, songid_t song) {
  1631. if(n >= len) { while(n >= len) { dev->addTrackToPlaylist(pl,song); len++; } return len; }
  1632. else {
  1633. dev->addTrackToPlaylist(pl,song);
  1634. dev->playlistSwapItems(pl,len,n);
  1635. dev->removeTrackFromPlaylist(pl,len);
  1636. }
  1637. return len;
  1638. }
  1639. */
  1640. class PlaylistSyncCopyInst : public CopyInst
  1641. {
  1642. public:
  1643. bool memFreed;
  1644. C_ItemList *songMaps;
  1645. C_ItemList * playlists;
  1646. PlaylistSyncCopyInst(DeviceView *dev, C_ItemList *songMaps, C_ItemList * playlists) : songMaps(songMaps), playlists(playlists)
  1647. {
  1648. usesPreCopy = false;
  1649. usesPostCopy = true;
  1650. this->dev = dev;
  1651. equalsType = -1;
  1652. status=STATUS_WAITING;
  1653. // status caption
  1654. WASABI_API_LNGSTRINGW_BUF(IDS_WAITING,statusCaption,sizeof(statusCaption)/sizeof(wchar_t));
  1655. // track caption
  1656. WASABI_API_LNGSTRINGW_BUF(IDS_PLAYLIST_SYNCRONIZATION,trackCaption,sizeof(trackCaption)/sizeof(wchar_t));
  1657. // type caption
  1658. WASABI_API_LNGSTRINGW_BUF(IDS_OTHER,typeCaption,sizeof(typeCaption)/sizeof(wchar_t));
  1659. memFreed=false;
  1660. }
  1661. virtual ~PlaylistSyncCopyInst()
  1662. {
  1663. freeMemory();
  1664. }
  1665. virtual bool CopyAction()
  1666. {
  1667. return false;
  1668. }
  1669. virtual void PostCopyAction()
  1670. {
  1671. SyncPlaylists(); freeMemory();
  1672. }
  1673. virtual void Cancelled()
  1674. {
  1675. freeMemory();
  1676. }
  1677. virtual bool Equals(CopyInst * b)
  1678. {
  1679. return false;
  1680. }
  1681. void freeMemory()
  1682. {
  1683. if(memFreed) return;
  1684. memFreed=true;
  1685. if(songMaps) delete songMaps;
  1686. songMaps = NULL;
  1687. if(playlists)
  1688. {
  1689. int l = playlists->GetSize();
  1690. for(int i=0; i<l; i++)
  1691. {
  1692. SyncPlaylist * playlist = (SyncPlaylist *)playlists->Get(i);
  1693. if(playlist)
  1694. {
  1695. if(playlist->songs)
  1696. {
  1697. for(int j=0; j<playlist->info.numItems; j++)
  1698. {
  1699. if(playlist->songs[j].ice)
  1700. {
  1701. freeRecord(playlist->songs[j].ice);
  1702. free(playlist->songs[j].ice);
  1703. }
  1704. if(playlist->songs[j].filename)
  1705. free(playlist->songs[j].filename);
  1706. }
  1707. free(playlist->songs);
  1708. }
  1709. free(playlist);
  1710. }
  1711. }
  1712. delete playlists;
  1713. playlists = NULL;
  1714. }
  1715. }
  1716. void SyncPlaylists()
  1717. {
  1718. if(memFreed)
  1719. return;
  1720. WASABI_API_LNGSTRINGW_BUF(IDS_WORKING,statusCaption,sizeof(statusCaption)/sizeof(wchar_t));
  1721. TransfersListUpdateItem(this);
  1722. TransfersListUpdateItem(this, dev);
  1723. MapItemRecordsToSongs(dev->dev,(PlaylistAddItem **)songMaps->GetAll(),songMaps->GetSize());
  1724. int numPlaylists = playlists->GetSize();
  1725. for(int i=0; i<numPlaylists; i++)
  1726. {
  1727. SyncPlaylist * playlist = (SyncPlaylist *)playlists->Get(i);
  1728. int plnum = -1;
  1729. bool done = false;
  1730. int l = dev->dev->getPlaylistCount();
  1731. int j;
  1732. for(j=0; j < l; j++)
  1733. {
  1734. wchar_t buf[128] = {0};
  1735. dev->dev->getPlaylistName(j,buf,128);
  1736. if(wcscmp(buf,playlist->info.playlistName)) continue;
  1737. int plen = dev->dev->getPlaylistLength(j);
  1738. if(plen != playlist->info.numItems)
  1739. {
  1740. plnum = j;
  1741. break;
  1742. }
  1743. for(int k=0; k<plen; k++)
  1744. {
  1745. if(playlist->songs[k].song != dev->dev->getPlaylistTrack(j,k))
  1746. {
  1747. plnum = j;
  1748. break;
  1749. }
  1750. }
  1751. if(plnum == -1)
  1752. {
  1753. done = true;
  1754. break;
  1755. }
  1756. }
  1757. if(done) continue;
  1758. if(plnum == -1)
  1759. {
  1760. plnum = dev->CreatePlaylist(playlist->info.playlistName,true);
  1761. if(plnum == -1) continue;
  1762. }
  1763. int plen = dev->dev->getPlaylistLength(plnum);
  1764. while(plen && ((plen % 4) != 1)) dev->dev->removeTrackFromPlaylist(plnum,--plen); // avoid granulation boundarys
  1765. int n=0;
  1766. for(j=0; j<playlist->info.numItems; j++)
  1767. {
  1768. songid_t s = playlist->songs[j].song;
  1769. if(s && (n>=plen || s != dev->dev->getPlaylistTrack(plnum,n)))
  1770. {
  1771. // begin set item code...
  1772. if(n >= plen) while(n >= plen)
  1773. {
  1774. dev->dev->addTrackToPlaylist(plnum,s);
  1775. plen++;
  1776. }
  1777. else
  1778. {
  1779. dev->dev->addTrackToPlaylist(plnum,s);
  1780. dev->dev->playlistSwapItems(plnum,plen,n);
  1781. dev->dev->removeTrackFromPlaylist(plnum,plen);
  1782. }
  1783. // end set item code
  1784. }
  1785. if(s) n++;
  1786. }
  1787. plen = dev->dev->getPlaylistLength(plnum);
  1788. while(plen > n) dev->dev->removeTrackFromPlaylist(plnum,--plen);
  1789. if(_wcsicmp(playlist->info.playlistName,L"Podcasts")==0)
  1790. {
  1791. wchar_t *name=NULL;
  1792. for(int j=playlist->info.numItems-1; j>=0; j--)
  1793. {
  1794. wchar_t *n = getRecordExtendedItem(playlist->songs[j].ice,L"podcastchannel");
  1795. if(!name) name=n;
  1796. if(name && n)
  1797. {
  1798. if(_wcsicmp(name,n))
  1799. {
  1800. dev->dev->extraActions(DEVICE_ADDPODCASTGROUP,plnum,j+1,(intptr_t)name);
  1801. name=n;
  1802. }
  1803. if(j==0) dev->dev->extraActions(DEVICE_ADDPODCASTGROUP,plnum,0,(intptr_t)name);
  1804. }
  1805. }
  1806. dev->dev->extraActions(DEVICE_ADDPODCASTGROUP_FINISH,plnum,0,0);
  1807. }
  1808. }
  1809. dev->DevicePropertiesChanges();
  1810. freeMemory();
  1811. WASABI_API_LNGSTRINGW_BUF(IDS_DONE,statusCaption,sizeof(statusCaption)/sizeof(wchar_t));
  1812. TransfersListUpdateItem(this);
  1813. TransfersListUpdateItem(this, dev);
  1814. }
  1815. };
  1816. static bool shouldSyncPlaylist(wchar_t * name, C_Config * config)
  1817. {
  1818. wchar_t buf[150] = {0};
  1819. StringCchPrintf(buf,150, L"sync-%s",name);
  1820. return config->ReadInt(buf,0) == config->ReadInt(L"plsyncwhitelist",1);
  1821. }
  1822. static int sortfunc_podcastpubdate(const void *elem1, const void *elem2)
  1823. {
  1824. itemRecordW *ar = (itemRecordW *)elem1;
  1825. itemRecordW *br = (itemRecordW *)elem2;
  1826. wchar_t *a = getRecordExtendedItem(ar,L"podcastpubdate");
  1827. wchar_t *b = getRecordExtendedItem(br,L"podcastpubdate");
  1828. if(!a) a = L"0";
  1829. if(!b) b = L"0";
  1830. return _wtoi(b) - _wtoi(a);
  1831. }
  1832. void DeviceView::OnActivityStarted()
  1833. {
  1834. for ( ifc_deviceevent *l_event_handler : event_handlers )
  1835. l_event_handler->ActivityStarted( this, this );
  1836. }
  1837. void DeviceView::OnActivityChanged()
  1838. {
  1839. for ( ifc_deviceevent *l_event_handler : event_handlers )
  1840. l_event_handler->ActivityChanged( this, this );
  1841. }
  1842. void DeviceView::OnActivityFinished()
  1843. {
  1844. for ( ifc_deviceevent *l_event_handler : event_handlers )
  1845. l_event_handler->ActivityFinished( this, this );
  1846. }
  1847. void DeviceView::UpdateActivityState()
  1848. {
  1849. LinkedQueue * txQueue = getTransferQueue(this);
  1850. if (txQueue && FALSE == activityRunning)
  1851. {
  1852. if (0 != txQueue->GetSize())
  1853. {
  1854. activityRunning = TRUE;
  1855. if (FAILED(GetProgress(&currentProgress)))
  1856. currentProgress = 0;
  1857. OnActivityStarted();
  1858. }
  1859. }
  1860. else
  1861. {
  1862. if (txQueue && 0 == txQueue->GetSize())
  1863. {
  1864. activityRunning = FALSE;
  1865. OnActivityFinished();
  1866. }
  1867. else
  1868. {
  1869. unsigned int percent;
  1870. if (FAILED(GetProgress(&percent)) ||
  1871. percent != currentProgress)
  1872. {
  1873. currentProgress = percent;
  1874. OnActivityChanged();
  1875. }
  1876. }
  1877. }
  1878. }
  1879. void DeviceView::UpdateSpaceInfo(BOOL updateUsedSpace, BOOL notifyChanges)
  1880. {
  1881. uint64_t total, used;
  1882. unsigned int changes;
  1883. changes = 0;
  1884. total = dev->getDeviceCapacityTotal();
  1885. if (total != totalSpace)
  1886. {
  1887. totalSpace = total;
  1888. changes |= (1 << 0);
  1889. }
  1890. if (FALSE != updateUsedSpace)
  1891. {
  1892. used = dev->getDeviceCapacityAvailable();
  1893. if (used > total)
  1894. used = total;
  1895. used = total - used;
  1896. if (used != usedSpace)
  1897. {
  1898. usedSpace = used;
  1899. changes |= (1 << 1);
  1900. }
  1901. }
  1902. if (0 != changes && FALSE != notifyChanges)
  1903. {
  1904. for ( ifc_deviceevent *l_event_handler : event_handlers )
  1905. {
  1906. if (0 != ((1 << 0) & changes))
  1907. l_event_handler->TotalSpaceChanged(this, totalSpace);
  1908. if (0 != ((1 << 1) & changes))
  1909. l_event_handler->TotalSpaceChanged(this, usedSpace);
  1910. }
  1911. }
  1912. }
  1913. void DeviceView::OnNameChanged(const wchar_t *new_name)
  1914. {
  1915. for ( ifc_deviceevent *l_event_handler : event_handlers )
  1916. l_event_handler->DisplayNameChanged( this, new_name );
  1917. }
  1918. void DeviceView::Sync(bool silent)
  1919. {
  1920. // sync configuration settings....
  1921. bool syncAllLibrary = config->ReadInt(L"syncAllLibrary",1)!=0;
  1922. if (AGAVE_API_STATS)
  1923. {
  1924. wchar_t device_name[128] = {0};
  1925. device_name[0] = 0;
  1926. if(dev->extraActions(DEVICE_GET_MODEL, (intptr_t)device_name, 128, 0) == 1 && device_name[0])
  1927. {
  1928. AGAVE_API_STATS->SetString("pmp", device_name);
  1929. }
  1930. }
  1931. HWND centerWindow = CENTER_OVER_ML_VIEW;
  1932. UpdateActivityState();
  1933. C_ItemList mllist;
  1934. wchar_t * querystring=0;
  1935. itemRecordListW *results = 0;
  1936. if(syncAllLibrary)
  1937. {
  1938. querystring = _wcsdup(config->ReadString(L"SyncQuery",L"type=0"));
  1939. results = (AGAVE_API_MLDB ? AGAVE_API_MLDB->Query(querystring) : NULL);
  1940. if (results)
  1941. for(int i = 0; i < results->Size; i++) mllist.Add(&results->Items[i]);
  1942. }
  1943. // read playlists/views and find out what else needs to be added
  1944. PlaylistSyncCopyInst * sync = NULL;
  1945. C_ItemList filenameMaps;
  1946. C_ItemList *songMaps = new C_ItemList;
  1947. C_ItemList * playlists = new C_ItemList;
  1948. // first collect playlists without metadata
  1949. SyncItemListLoader list;
  1950. list.metaToGet = &filenameMaps;
  1951. list.songMaps = songMaps;
  1952. int playlistsnum = SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_PLAYLIST_COUNT);
  1953. for(int i=0; i<playlistsnum; i++)
  1954. {
  1955. SyncPlaylist* playlist = (SyncPlaylist*)calloc(sizeof(SyncPlaylist),1);
  1956. playlist->info.size = sizeof(mlPlaylistInfo);
  1957. playlist->info.playlistNum = i;
  1958. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&playlist->info, ML_IPC_PLAYLIST_INFO);
  1959. if(shouldSyncPlaylist(playlist->info.playlistName, config))
  1960. {
  1961. playlists->Add(playlist);
  1962. }
  1963. else
  1964. {
  1965. free(playlist);
  1966. playlist = 0;
  1967. continue;
  1968. }
  1969. //if(playlist->info.numItems <= 1)
  1970. {
  1971. list.pos = list.len = 0;
  1972. playlistManager->Load(playlist->info.filename, &list);
  1973. playlist->info.numItems = list.pos;
  1974. }
  1975. list.pos = 0;
  1976. list.len=playlist->info.numItems;
  1977. list.songs = playlist->songs = (songMapping*)calloc(sizeof(songMapping), list.len);
  1978. playlistManager->Load(playlist->info.filename, &list);
  1979. }
  1980. mapFilesToItemRecords((filenameMap **)filenameMaps.GetAll(), filenameMaps.GetSize(), centerWindow); // get metadata
  1981. // now sync podcasts...
  1982. if (dev->extraActions(DEVICE_SUPPORTS_PODCASTS, 0, 0, 0) == 0)
  1983. {
  1984. int podcasteps = config->ReadInt(L"podcast-sync_episodes",0);
  1985. int podcastsnum = AGAVE_API_PODCASTS ? AGAVE_API_PODCASTS->GetNumPodcasts() : 0;
  1986. if(podcasteps && podcastsnum > 0)
  1987. {
  1988. // if we want to sync podcasts and we have podcasts to sync
  1989. bool all = !!config->ReadInt(L"podcast-sync_all", 1);
  1990. SyncPlaylist * s = (SyncPlaylist *)calloc(sizeof(SyncPlaylist),1);
  1991. lstrcpyn(s->info.playlistName, L"Podcasts", 128); //set the name of the playlist containing our podcasts
  1992. int n = 0, alloc = 512;
  1993. s->songs = (songMapping*)calloc(alloc, sizeof(songMapping));
  1994. for(int i = 0; i < podcastsnum; i++)
  1995. {
  1996. ifc_podcast *podcast = AGAVE_API_PODCASTS->EnumPodcast(i);
  1997. if(podcast)
  1998. {
  1999. wchar_t podcast_name[256] = {0};
  2000. if(podcast->GetTitle(podcast_name, 256) == 0)
  2001. {
  2002. wchar_t buf[300] = {0};
  2003. StringCchPrintf(buf, 300, L"podcast-sync-%s", podcast_name);
  2004. if(podcast_name[0] && (all || config->ReadInt(buf,0))) // if we have a podcast and we want to sync it
  2005. {
  2006. wchar_t query[300] = {0};
  2007. StringCchPrintf(query, 300, L"podcastchannel = \"%s\"", podcast_name);
  2008. itemRecordListW *podcasts = AGAVE_API_MLDB->Query(query);
  2009. if(podcasts)
  2010. {
  2011. qsort(podcasts->Items,podcasts->Size,sizeof(itemRecordW),sortfunc_podcastpubdate); // sort the podcasts into publish date order
  2012. for(int j=0; j<podcasts->Size && (podcasteps == -1 || j < podcasteps); j++)
  2013. {
  2014. // add podcast to playlist
  2015. if(n >= alloc)
  2016. {
  2017. size_t old_alloc = alloc;
  2018. alloc += 512;
  2019. songMapping* new_songs = (songMapping*)realloc(s->songs,sizeof(songMapping) * alloc);
  2020. if (new_songs)
  2021. {
  2022. s->songs = new_songs;
  2023. }
  2024. else
  2025. {
  2026. new_songs = (songMapping*)malloc(sizeof(songMapping) * alloc);
  2027. if (new_songs)
  2028. {
  2029. memcpy(new_songs, s->songs, sizeof(songMapping) * old_alloc);
  2030. free(s->songs);
  2031. s->songs = new_songs;
  2032. }
  2033. else
  2034. {
  2035. alloc = old_alloc;
  2036. continue;
  2037. }
  2038. }
  2039. }
  2040. ZeroMemory(&s->songs[n],sizeof(songMapping));
  2041. s->songs[n].ice = (itemRecordW*)calloc(sizeof(itemRecordW), 1);
  2042. copyRecord(s->songs[n].ice,&podcasts->Items[j]);
  2043. mllist.Add(s->songs[n].ice);
  2044. songMaps->Add(&s->songs[n].pladd);
  2045. n++;
  2046. }
  2047. if(podcasts)
  2048. AGAVE_API_MLDB->FreeRecordList(podcasts);
  2049. }
  2050. }
  2051. }
  2052. }
  2053. }
  2054. s->info.numItems = n;
  2055. if(n)
  2056. playlists->Add(s);
  2057. else
  2058. {
  2059. free(s->songs);
  2060. free(s);
  2061. }
  2062. }
  2063. }
  2064. // now collect playlists with metadata (i.e, smart views)
  2065. // except the new ml_local isn't ready.
  2066. // calloc a new SyncPlaylist, fill in playlist->info.numItems, playlist->info.playlistName and playlist->songs[].ice then add to playlists.
  2067. // add tracks to be sync'd
  2068. for(int i=0; i<filenameMaps.GetSize(); i++)
  2069. {
  2070. filenameMap* f = (filenameMap*)filenameMaps.Get(i);
  2071. if(f->ice)
  2072. mllist.Add(f->ice);
  2073. }
  2074. // prepare sync
  2075. if(playlists->GetSize())
  2076. sync = new PlaylistSyncCopyInst(this, songMaps, playlists);
  2077. else
  2078. {
  2079. delete playlists;
  2080. delete songMaps;
  2081. }
  2082. // work out the tracks to be sent and deleted...
  2083. C_ItemList synclist,dellist;
  2084. ProcessDatabaseDifferences(dev, &mllist, NULL, &synclist, NULL, &dellist);
  2085. if(!synclist.GetSize() && !dellist.GetSize())
  2086. {
  2087. // nothing to do
  2088. if(sync)
  2089. {
  2090. sync->SyncPlaylists();
  2091. delete sync;
  2092. }
  2093. if(!silent)
  2094. {
  2095. wchar_t titleStr[32] = {0};
  2096. MessageBox(plugin.hwndLibraryParent,
  2097. WASABI_API_LNGSTRINGW(IDS_NOTHING_TO_SYNC_UP_TO_DATE),
  2098. WASABI_API_LNGSTRINGW_BUF(IDS_SYNC, titleStr, 32),0);
  2099. }
  2100. }
  2101. else
  2102. {
  2103. // need to sync some tracks
  2104. if(IDOK == SyncDialog_Show(centerWindow, this, &synclist, &dellist, FALSE))
  2105. {
  2106. config->WriteInt(L"syncOnConnect_time",(int)time(NULL));
  2107. if(dellist.GetSize())
  2108. {
  2109. switch(config->ReadInt(L"TrueSync",0))
  2110. {
  2111. case 1: this->DeleteTracks(&dellist, centerWindow); break;
  2112. case 2: this->CopyTracksToHardDrive(&dellist); break;
  2113. }
  2114. }
  2115. int i = 0, l = 0;
  2116. LinkedQueue * txQueue = getTransferQueue(this);
  2117. if (txQueue)
  2118. {
  2119. l = synclist.GetSize();
  2120. txQueue->lock();
  2121. for(i = 0; i < l; i++) if(AddTrackToTransferQueue(this, (itemRecordW*)synclist.Get(i), false) == -1) break;
  2122. if(sync) AddTrackToTransferQueue(sync);
  2123. txQueue->unlock();
  2124. }
  2125. if(i != l)
  2126. {
  2127. wchar_t titleStr[128] = {0};
  2128. MessageBox(plugin.hwndLibraryParent,
  2129. WASABI_API_LNGSTRINGW(IDS_THERE_IS_NOT_ENOUGH_SPACE_ON_THE_DEVICE),
  2130. WASABI_API_LNGSTRINGW_BUF(IDS_NOT_ENOUGH_SPACE, titleStr, ARRAYSIZE(titleStr)),
  2131. MB_OK | MB_ICONWARNING);
  2132. }
  2133. }
  2134. else
  2135. {
  2136. if(sync) delete sync;
  2137. }
  2138. }
  2139. if(syncAllLibrary)
  2140. {
  2141. if(results)
  2142. AGAVE_API_MLDB->FreeRecordList(results);
  2143. free(querystring);
  2144. }
  2145. }
  2146. void DeviceView::CloudSync(bool silent)
  2147. {
  2148. if (AGAVE_API_STATS)
  2149. {
  2150. wchar_t device_name[128] = {0};
  2151. if(dev->extraActions(DEVICE_GET_MODEL, (intptr_t)device_name, 128, 0) == 1 && device_name[0])
  2152. {
  2153. AGAVE_API_STATS->SetString("pmp", device_name);
  2154. }
  2155. }
  2156. UpdateActivityState();
  2157. // work out the tracks to be sent...
  2158. C_ItemList *filenameMaps2 = new C_ItemList, synclist;
  2159. DeviceView * hss = 0, * local = 0;
  2160. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  2161. if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
  2162. {
  2163. winampMediaLibraryPlugin *(*gp)();
  2164. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
  2165. if (gp)
  2166. {
  2167. winampMediaLibraryPlugin *mlplugin = gp();
  2168. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  2169. {
  2170. // determine the cloud device and alter the device
  2171. // to be checked with as needed by the action done
  2172. for(int i = 0; i < devices.GetSize(); i++)
  2173. {
  2174. DeviceView * d = (DeviceView *)devices.Get(i);
  2175. if (d->isCloudDevice)
  2176. {
  2177. char name[128] = {0};
  2178. if (d->dev->extraActions(DEVICE_GET_UNIQUE_ID, (intptr_t)name, sizeof(name), 0))
  2179. {
  2180. if (!strcmp(name, "hss"/*HSS_CLIENT*/))
  2181. hss = d;
  2182. else if (!strcmp(name, "local_desktop"))
  2183. local = d;
  2184. }
  2185. }
  2186. }
  2187. if (local && hss && local->dev == dev)
  2188. {
  2189. // just use the local library as the source to compare against
  2190. mlplugin->MessageProc(0x403, /*source*/-1, /*dest*/hss->dev->extraActions(DEVICE_GET_CLOUD_DEVICE_ID,0,0,0), (INT_PTR)filenameMaps2);
  2191. }
  2192. else
  2193. {
  2194. // just use the local library as the source to compare against
  2195. mlplugin->MessageProc(0x403, /*source*/-1, /*dest*/dev->extraActions(DEVICE_GET_CLOUD_DEVICE_ID,0,0,0), (INT_PTR)filenameMaps2);
  2196. }
  2197. }
  2198. }
  2199. }
  2200. synclist = *fileListToItemRecords(filenameMaps2, CENTER_OVER_ML_VIEW);
  2201. nu::qsort(synclist.GetAll(), synclist.GetSize(), sizeof(void*), dev, compareSongs);
  2202. if(!synclist.GetSize())
  2203. {
  2204. if(!silent)
  2205. {
  2206. wchar_t titleStr[32] = {0};
  2207. MessageBox(plugin.hwndLibraryParent,
  2208. WASABI_API_LNGSTRINGW(IDS_NOTHING_TO_SYNC_UP_TO_DATE),
  2209. WASABI_API_LNGSTRINGW_BUF(IDS_SYNC,titleStr,32),0);
  2210. }
  2211. }
  2212. else
  2213. {
  2214. DeviceView * destDevice = (local && hss && local->dev == dev ? hss : this);
  2215. // need to sync some tracks
  2216. if(IDOK == SyncCloudDialog_Show(CENTER_OVER_ML_VIEW, destDevice, &synclist))
  2217. {
  2218. int l = synclist.GetSize();
  2219. cloudTransferQueue.lock();
  2220. int i = 0;
  2221. for (; i < l; i++) if (AddTrackToTransferQueue(destDevice, (itemRecordW*)synclist.Get(i), false) == -1) break;
  2222. cloudTransferQueue.unlock();
  2223. if(i != l)
  2224. {
  2225. wchar_t titleStr[128] = {0};
  2226. MessageBox(plugin.hwndLibraryParent,
  2227. WASABI_API_LNGSTRINGW(IDS_THERE_IS_NOT_ENOUGH_SPACE_ON_THE_DEVICE),
  2228. WASABI_API_LNGSTRINGW_BUF(IDS_NOT_ENOUGH_SPACE, titleStr, ARRAYSIZE(titleStr)),
  2229. MB_OK | MB_ICONWARNING);
  2230. }
  2231. }
  2232. }
  2233. }
  2234. extern itemRecordListW * generateAutoFillList(DeviceView * dev, C_Config * config); // from autofill.cpp
  2235. void DeviceView::Autofill()
  2236. {
  2237. HWND centerWindow;
  2238. centerWindow = CENTER_OVER_ML_VIEW;
  2239. if (AGAVE_API_STATS)
  2240. {
  2241. wchar_t device_name[128] = {0};
  2242. if(dev->extraActions(DEVICE_GET_MODEL, (intptr_t)device_name, 128, 0) == 1 && device_name[0])
  2243. {
  2244. AGAVE_API_STATS->SetString("pmp", device_name);
  2245. }
  2246. }
  2247. UpdateActivityState();
  2248. C_ItemList delList,sendList;
  2249. itemRecordListW * autofillList = generateAutoFillList(this,config);
  2250. ProcessDatabaseDifferences(dev,autofillList,NULL,&sendList,NULL,&delList);
  2251. if(IDOK == SyncDialog_Show(centerWindow, this, &sendList, &delList, TRUE))
  2252. {
  2253. config->WriteInt(L"syncOnConnect_time", (int)time(NULL));
  2254. // delete all tracks in delList
  2255. if(IDOK == DeleteTracks(&delList, centerWindow))
  2256. {
  2257. // not aborted
  2258. // send all tracks in sendList
  2259. LinkedQueue * txQueue = getTransferQueue(this);
  2260. if (txQueue)
  2261. {
  2262. txQueue->lock();
  2263. for(int i = 0; i < sendList.GetSize(); i++) AddTrackToTransferQueue(this, (itemRecordW*)sendList.Get(i), false);
  2264. txQueue->unlock();
  2265. }
  2266. }
  2267. }
  2268. if(autofillList)
  2269. freeRecordList(autofillList);
  2270. }
  2271. extern int serverPort;
  2272. bool DeviceView::PlayTracks(C_ItemList * tracks, int startPlaybackAt, bool enqueue, bool msgIfImpossible, HWND parent)
  2273. {
  2274. if(tracks->GetSize() == 0) return true;
  2275. // direct playback?
  2276. if(dev->playTracks((songid_t*)tracks->GetAll(),tracks->GetSize(),startPlaybackAt,enqueue))
  2277. return true;
  2278. if(serverPort>0 && dev->copyToHardDriveSupported())
  2279. {
  2280. // indirect playback?
  2281. if(!enqueue)
  2282. {
  2283. //clear playlist
  2284. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_DELETE);
  2285. }
  2286. wchar_t buf[2048] = {0};
  2287. dev->getPlaylistName(0,buf,128);
  2288. AutoUrl device(buf);
  2289. for(int i=0; i<tracks->GetSize(); i++)
  2290. {
  2291. songid_t s = (songid_t)tracks->Get(i);
  2292. //encode fields to url format
  2293. wchar_t metadata[2048] = {0};
  2294. dev->getTrackArtist(s,metadata,2048);
  2295. AutoUrl artist(metadata);
  2296. dev->getTrackAlbum(s,metadata,2048);
  2297. AutoUrl album(metadata);
  2298. dev->getTrackTitle(s,metadata,2048);
  2299. AutoUrl title(metadata);
  2300. // construct URL
  2301. wchar_t ext[10]=L"";
  2302. dev->getTrackExtraInfo(s,L"ext",ext,10);
  2303. char buf[8192] = {0};
  2304. StringCchPrintfA(buf,8192, "http://127.0.0.1:%d/?a=%s&l=%s&t=%s&d=%s%s%s",serverPort,artist,album,title,device,*ext?";.":"",(char*)AutoChar(ext));
  2305. // get title
  2306. AutoWide wideUrl(buf);
  2307. wchar_t buf2[4096] = {0};
  2308. getTitle(dev,s,wideUrl,buf2,4096);
  2309. // enqueue file
  2310. enqueueFileWithMetaStructW ef = { wideUrl, buf2, NULL, dev->getTrackLength( s ) / 1000 };
  2311. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&ef, IPC_PLAYFILEW);
  2312. }
  2313. if(!enqueue) //play item startPlaybackAt
  2314. {
  2315. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,startPlaybackAt,IPC_SETPLAYLISTPOS);
  2316. SendMessage(plugin.hwndWinampParent,WM_COMMAND,40047,0); //stop
  2317. SendMessage(plugin.hwndWinampParent,WM_COMMAND,40045,0); //play
  2318. }
  2319. return true;
  2320. }
  2321. if(msgIfImpossible)
  2322. {
  2323. wchar_t titleStr[32] = {0};
  2324. MessageBox(parent,WASABI_API_LNGSTRINGW(IDS_DOES_NOT_SUPPORT_DIRECT_PLAYBACK),
  2325. WASABI_API_LNGSTRINGW_BUF(IDS_UNSUPPORTED,titleStr,32),0);
  2326. }
  2327. return false;
  2328. }
  2329. bool DeviceView::PlayPlaylist(int playlistId, bool enqueue, bool msgIfImpossible, HWND parent)
  2330. {
  2331. int l = dev->getPlaylistLength(playlistId);
  2332. C_ItemList tracks;
  2333. for(int j=0; j<l; j++)
  2334. tracks.Add((void*)dev->getPlaylistTrack(playlistId,j));
  2335. return PlayTracks(&tracks, 0, enqueue, msgIfImpossible, parent);
  2336. }
  2337. void DeviceView::CopyTracksToHardDrive(C_ItemList * tracks)
  2338. {
  2339. CopyTracksToHardDrive((songid_t*)tracks->GetAll(),tracks->GetSize());
  2340. }
  2341. static void getReverseCopyFilenameFormat(wchar_t* filepath, wchar_t* format, int len, BOOL * uppercaseext)
  2342. {
  2343. wchar_t m_def_extract_path[MAX_PATH] = L"C:\\My Music";
  2344. wchar_t m_def_filename_fmt[MAX_PATH] = L"<Artist> - <Album>\\## - <Trackartist> - <Title>";
  2345. GetDefaultSaveToFolder(m_def_extract_path);
  2346. bool cdrip = !!global_config->ReadInt(L"extractusecdrip", 1);
  2347. const wchar_t *mlinifile = (const wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW);
  2348. wchar_t buf[2048] = {0};
  2349. if(cdrip) GetPrivateProfileString(L"gen_ml_config",L"extractpath",m_def_extract_path,buf,2048,mlinifile);
  2350. else lstrcpyn(buf,global_config->ReadString(L"extractpath",m_def_extract_path),2048);
  2351. lstrcpyn(filepath,buf,len);
  2352. int l = wcslen(filepath);
  2353. if(*(filepath+l-1) != L'\\')
  2354. {
  2355. *(filepath+l) = L'\\';
  2356. *(filepath+l+1)=0;
  2357. l++;
  2358. }
  2359. if(cdrip) GetPrivateProfileString(L"gen_ml_config",L"extractfmt2",m_def_filename_fmt,buf,2048,mlinifile);
  2360. else lstrcpyn(buf,global_config->ReadString(L"extractfmt2",m_def_filename_fmt),2048);
  2361. if(l < len) lstrcpyn(format/*+l*/,buf,len - l);
  2362. if(cdrip) *uppercaseext = GetPrivateProfileInt(L"gen_ml_config",L"extractucext",0,mlinifile);
  2363. else *uppercaseext = global_config->ReadInt(L"extractucext",0);
  2364. }
  2365. void DeviceView::CopyTracksToHardDrive(songid_t * tracks, int numTracks)
  2366. {
  2367. if(!dev->copyToHardDriveSupported()) return;
  2368. BOOL uppercaseext=FALSE;
  2369. wchar_t filepath[MAX_PATH] = {0}, format[2048] = {0};
  2370. getReverseCopyFilenameFormat(filepath,format,2048,&uppercaseext);
  2371. LinkedQueue * txQueue = getTransferQueue(this);
  2372. if (txQueue)
  2373. {
  2374. txQueue->lock();
  2375. for(int i=0; i<numTracks; i++)
  2376. {
  2377. AddTrackToTransferQueue(new ReverseCopyInst(this,filepath,format,tracks[i],true,!!uppercaseext));
  2378. }
  2379. txQueue->unlock();
  2380. }
  2381. }
  2382. void DeviceView::CopyPlaylistToLibrary(int plnum)
  2383. {
  2384. if(plnum==0) return;
  2385. wchar_t name[128] = {0};
  2386. dev->getPlaylistName(plnum,name,128);
  2387. wchar_t filename[MAX_PATH] = {0};
  2388. wchar_t dir[MAX_PATH] = {0};
  2389. GetTempPath(MAX_PATH,dir);
  2390. GetTempFileName(dir,L"pmppl",0,filename);
  2391. _wunlink(filename);
  2392. {
  2393. wchar_t * ext = wcsrchr(filename,L'.');
  2394. if(ext) *ext=0;
  2395. StringCchCat(filename,MAX_PATH,L".m3u");
  2396. }
  2397. FILE * f = _wfopen(filename,L"wt"); if(f)
  2398. {
  2399. fputws(L"#EXTM3U\n",f);
  2400. fclose(f);
  2401. }
  2402. /*
  2403. mlMakePlaylistV2 a = {sizeof(mlMakePlaylistV2),name,ML_TYPE_FILENAMES,"\0\0",PL_FLAG_SHOW | PL_FLAG_FILL_FILENAME};
  2404. SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&a,ML_IPC_PLAYLIST_MAKE);
  2405. */
  2406. wchar_t filepath[MAX_PATH] = {0}, format[2048] = {0};
  2407. BOOL uppercaseext=FALSE;
  2408. getReverseCopyFilenameFormat(filepath,format,2048,&uppercaseext);
  2409. int l = dev->getPlaylistLength(plnum);
  2410. LinkedQueue * txQueue = getTransferQueue(this);
  2411. if (txQueue)
  2412. {
  2413. txQueue->lock();
  2414. for(int i=0; i<l; i++)
  2415. AddTrackToTransferQueue(new ReversePlaylistCopyInst(this,filepath,format,dev->getPlaylistTrack(plnum,i),filename,name,i==l-1,true));
  2416. txQueue->unlock();
  2417. }
  2418. }
  2419. void DeviceView::Unregister()
  2420. {
  2421. for(size_t i=0; i < playlistTreeItems.size(); i++)
  2422. {
  2423. HNAVITEM item = playlistTreeItems[i];
  2424. // TODO: free memory associated with the text for item
  2425. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, item);
  2426. }
  2427. playlistTreeItems.clear();
  2428. if (videoTreeItem)
  2429. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, videoTreeItem);
  2430. videoTreeItem=0;
  2431. if (AGAVE_API_DEVICEMANAGER)
  2432. AGAVE_API_DEVICEMANAGER->DeviceUnregister(name);
  2433. if (treeItem)
  2434. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, treeItem);
  2435. treeItem=0;
  2436. }