sbMediaExportService.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 
26 #include "sbMediaExportService.h"
27 
28 #include <sbILibraryManager.h>
29 #include <sbILibrary.h>
30 #include <sbIMediaExportAgentService.h>
31 
32 #include <nsICategoryManager.h>
33 #include <nsIObserverService.h>
34 #include <nsIClassInfoImpl.h>
35 #include <nsIProgrammingLanguage.h>
36 #include <nsIThread.h>
37 #include <nsIProxyObjectManager.h>
38 #include <nsIRunnable.h>
39 #include <nsIThreadManager.h>
40 #include <nsThreadUtils.h>
41 #include <nsIThreadPool.h>
42 #include <nsIUpdateService.h>
43 
44 #include <nsAutoPtr.h>
45 #include <nsArrayUtils.h>
46 #include <nsDirectoryServiceUtils.h>
47 #include <nsComponentManagerUtils.h>
48 #include <nsServiceManagerUtils.h>
49 #include <nsMemory.h>
50 
51 #include <sbLibraryUtils.h>
52 #include <sbPropertiesCID.h>
53 #include <sbStandardProperties.h>
54 #include <sbStringUtils.h>
55 #include <sbThreadPoolService.h>
57 #include <sbDebugUtils.h>
58 
59 #include <algorithm>
60 
61 //------------------------------------------------------------------------------
62 // To log this module, set the following environment variable:
63 // NSPR_LOG_MODULES=sbMediaExportService:5
64 
65 template <class T>
66 static nsresult
67 EnumerateItemsByGuids(typename T::const_iterator const aGuidStringListBegin,
68  typename T::const_iterator const aGuidStringListEnd,
69  sbIMediaList *aMediaList,
70  nsIArray **aRetVal)
71 {
72  NS_ENSURE_ARG_POINTER(aMediaList);
73  NS_ENSURE_ARG_POINTER(aRetVal);
74 
75  nsresult rv;
76 
77 #ifdef PR_LOGGING
78  nsString mediaListName;
79  rv = aMediaList->GetName(mediaListName);
80  NS_ENSURE_SUCCESS(rv, rv);
81  LOG("%s: Enumerate items by guids on media list '%s'",
82  __FUNCTION__, NS_ConvertUTF16toUTF8(mediaListName).get());
83 #endif
84 
85  nsCOMPtr<sbIMutablePropertyArray> properties =
86  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
87  NS_ENSURE_SUCCESS(rv, rv);
88 
89  NS_NAMED_LITERAL_STRING(guidProperty, SB_PROPERTY_GUID);
90 
91  typename T::const_iterator iter;
92  for (iter = aGuidStringListBegin; iter != aGuidStringListEnd; ++iter) {
93  rv = properties->AppendProperty(guidProperty, *iter);
94  NS_ENSURE_SUCCESS(rv, rv);
95  }
96 
97  nsRefPtr<sbMediaListEnumArrayHelper> enumHelper =
99  NS_ENSURE_TRUE(enumHelper, NS_ERROR_OUT_OF_MEMORY);
100 
101  rv = aMediaList->EnumerateItemsByProperties(
102  properties,
103  enumHelper,
105  NS_ENSURE_SUCCESS(rv, rv);
106 
107  return enumHelper->GetMediaItemsArray(aRetVal);
108 }
109 
110 //------------------------------------------------------------------------------
111 
114  nsIClassInfo,
115  nsIObserver,
120 
123  nsIClassInfo,
124  nsIObserver,
127 
128 NS_DECL_CLASSINFO(sbMediaExportService)
129 NS_IMPL_THREADSAFE_CI(sbMediaExportService)
130 
131 //------------------------------------------------------------------------------
132 
133 sbMediaExportService::sbMediaExportService()
134  : mIsRunning(PR_FALSE)
135  , mTotal(0)
136  , mProgress(0)
137 {
138  SB_PRLOG_SETUP(sbMediaExportService);
139 }
140 
142 {
143 }
144 
145 nsresult
147 {
148  LOG("%s: Starting the export service", __FUNCTION__);
149 
150  nsresult rv;
151  nsCOMPtr<nsIObserverService> observerService =
152  do_GetService("@mozilla.org/observer-service;1", &rv);
153  NS_ENSURE_SUCCESS(rv, rv);
154 
155  rv = observerService->AddObserver(this,
157  PR_FALSE);
158  NS_ENSURE_SUCCESS(rv, rv);
159 
160  rv = observerService->AddObserver(this,
162  PR_FALSE);
163  NS_ENSURE_SUCCESS(rv, rv);
164 
165  rv = observerService->AddObserver(this, "songbird-shutdown", PR_FALSE);
166  NS_ENSURE_SUCCESS(rv, rv);
167 
168  // Create and start the pref controller
169  mPrefController = new sbMediaExportPrefController();
170  NS_ENSURE_TRUE(mPrefController, NS_ERROR_OUT_OF_MEMORY);
171 
172  rv = mPrefController->Init(this);
173  NS_ENSURE_SUCCESS(rv, rv);
174 
175  return NS_OK;
176 }
177 
178 nsresult
180 {
181  LOG("%s: Internal initializing the export service", __FUNCTION__);
182 
183  // Don't bother starting any listeners when the service should not run.
184  if (!mPrefController->GetShouldExportAnyMedia()) {
185  return NS_OK;
186  }
187 
188  mIsRunning = PR_TRUE;
189 
190  // At least one item needs to be exported, setup the library listener.
191  nsresult rv;
192  nsCOMPtr<sbILibrary> mainLibrary;
193  rv = GetMainLibrary(getter_AddRefs(mainLibrary));
194  NS_ENSURE_SUCCESS(rv, rv);
195 
196  rv = ListenToMediaList(mainLibrary);
197  NS_ENSURE_SUCCESS(rv, rv);
198 
199  // Find and listen to all playlists as needed.
200  if (mPrefController->GetShouldExportPlaylists() ||
201  mPrefController->GetShouldExportSmartPlaylists())
202  {
203  nsCOMPtr<nsIArray> foundPlaylists;
204  rv = mainLibrary->GetItemsByProperty(
205  NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
206  NS_LITERAL_STRING("1"),
207  getter_AddRefs(foundPlaylists));
208  NS_ENSURE_SUCCESS(rv, rv);
209 
210  PRUint32 length;
211  rv = foundPlaylists->GetLength(&length);
212  NS_ENSURE_SUCCESS(rv, rv);
213 
214  for (PRUint32 i = 0; i < length; i++) {
215  nsCOMPtr<sbIMediaList> curMediaList =
216  do_QueryElementAt(foundPlaylists, i, &rv);
217  if (NS_FAILED(rv) || !curMediaList) {
218  NS_WARNING("ERROR: Could not get the current playlist from an array!");
219  continue;
220  }
221 
222  PRBool shouldWatch = PR_FALSE;
223  rv = GetShouldWatchMediaList(curMediaList, &shouldWatch);
224  if (NS_SUCCEEDED(rv) && shouldWatch) {
225  rv = ListenToMediaList(curMediaList);
226  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
227  "ERROR: Could not start listening to a media list!");
228  }
229  }
230  }
231 
232  return NS_OK;
233 }
234 
235 nsresult
237 {
238  LOG("%s: Shutting down export service", __FUNCTION__);
239 
240  nsresult rv;
241  nsCOMPtr<nsIObserverService> observerService =
242  do_GetService("@mozilla.org/observer-service;1", &rv);
243  NS_ENSURE_SUCCESS(rv, rv);
244 
245  rv = observerService->RemoveObserver(this, SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC);
246  NS_ENSURE_SUCCESS(rv, rv);
247 
249  NS_ENSURE_SUCCESS(rv, rv);
250 
251  rv = mPrefController->Shutdown();
252  NS_ENSURE_SUCCESS(rv, rv);
253 
254  // Let's ask the update service if there are updates that need to be applied.
255  nsCOMPtr<nsIUpdateManager> updateMgr =
256  do_GetService("@mozilla.org/updates/update-manager;1", &rv);
257  NS_ENSURE_SUCCESS(rv, rv);
258 
259  PRBool hasPendingUpdates = PR_FALSE;
260  PRInt32 updateCount;
261  rv = updateMgr->GetUpdateCount(&updateCount);
262  NS_ENSURE_SUCCESS(rv, rv);
263 
264  LOG("%s: Found %i updates", __FUNCTION__, updateCount);
265 
266  if (updateCount > 0) {
267  for (PRInt32 i = 0; i < updateCount && !hasPendingUpdates; i++) {
268  nsCOMPtr<nsIUpdate> curUpdate;
269  rv = updateMgr->GetUpdateAt(i, getter_AddRefs(curUpdate));
270  if (NS_FAILED(rv)) {
271  continue;
272  }
273 
274  nsString state;
275  rv = curUpdate->GetState(state);
276  if (NS_FAILED(rv)) {
277  continue;
278  }
279 
280  // According to |nsIUpdate| in "nsIUpdateService.idl", the |state| field
281  // will contain the literal string "pending" when there are updates that
282  // will be applied.
283  if (state.EqualsLiteral("pending")) {
284  hasPendingUpdates = PR_TRUE;
285  }
286  }
287 
288  if (hasPendingUpdates) {
289  // When there is at least one update according to the update manager, the
290  // updater will run the next time Songbird is launched. In order to assist
291  // with updates, any currently running instances of the agent need to be
292  // killed.
293  nsCOMPtr<sbIMediaExportAgentService> agentService =
294  do_GetService(SB_MEDIAEXPORTAGENTSERVICE_CONTRACTID, &rv);
295  if (NS_SUCCEEDED(rv) && agentService) {
296  // First, kill the agent(s).
297  rv = agentService->KillActiveAgents();
298  NS_ENSURE_SUCCESS(rv, rv);
299 
300  // Next, unregister the agent. The agent will re-register for user login
301  // the next time it is run.
302  rv = agentService->UnregisterExportAgent();
303  NS_ENSURE_SUCCESS(rv, rv);
304  }
305  }
306  }
307 
308  return NS_OK;
309 }
310 
311 nsresult
313 {
314  LOG("%s: Removing listeners from all media lists", __FUNCTION__);
315 
316  nsresult rv;
317 
318  if (mIsRunning) {
319  // Removing listener references from all the observed media lists.
320  for (PRInt32 i = 0; i < mObservedMediaLists.Count(); i++) {
321  nsCOMPtr<sbIMediaList> curMediaList = mObservedMediaLists[i];
322  if (!curMediaList) {
323  NS_WARNING("Could not get a the media list reference!");
324  continue;
325  }
326 
327  rv = curMediaList->RemoveListener(this);
328  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
329  "Could not remove media list listener!");
330 
331 #if PR_LOGGING
332  nsString listGuid;
333  rv = curMediaList->GetGuid(listGuid);
334  NS_ENSURE_SUCCESS(rv, rv);
335  LOG("%s: Removing listener for media list '%s'",
336  __FUNCTION__, NS_ConvertUTF16toUTF8(listGuid).get());
337 #endif
338  }
339 
340  // Remove smart media list listener references from all the observed smart
341  // media lists.
342  for (PRInt32 i = 0; i < mObservedSmartMediaLists.Count(); i++) {
343  nsCOMPtr<sbILocalDatabaseSmartMediaList> curSmartList =
344  mObservedSmartMediaLists[i];
345  if (!curSmartList) {
346  NS_WARNING("Could not get a smart media list reference!");
347  continue;
348  }
349 
350  rv = curSmartList->RemoveSmartMediaListListener(this);
351  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
352  "Could not remove the smart media list listener!");
353 
354 #if PR_LOGGING
355  nsString listGuid;
356  rv = curSmartList->GetGuid(listGuid);
357  NS_ENSURE_SUCCESS(rv, rv);
358  LOG("%s: Removing listener for smart media list '%s'",
359  __FUNCTION__, NS_ConvertUTF16toUTF8(listGuid).get());
360 #endif
361  }
362 
363  mObservedMediaLists.Clear();
364  mObservedSmartMediaLists.Clear();
365 
366  // Clean up the exported data lists
367  mAddedItemsMap.clear();
368  mAddedMediaList.clear();
369  mRemovedMediaLists.clear();
370 
371  mIsRunning = PR_FALSE;
372  }
373 
374  return NS_OK;
375 }
376 
377 NS_IMETHODIMP
378 sbMediaExportService::OnBoolPrefChanged(const nsAString & aPrefName,
379  const PRBool aNewPrefValue)
380 {
381  LOG("%s: '%s' pref changed : %s",
382  __FUNCTION__,
383  NS_ConvertUTF16toUTF8(aPrefName).get(),
384  (aNewPrefValue ? "true" : "false"));
385 
386  nsresult rv;
387 
388  // If the user turned on some export prefs and the service is not running
389  // it is now time to start running.
390  if (!mIsRunning && mPrefController->GetShouldExportAnyMedia()) {
391  rv = InitInternal();
392  NS_ENSURE_SUCCESS(rv, rv);
393  }
394  // Shutdown if the service is currently running
395  else if (mIsRunning && !mPrefController->GetShouldExportAnyMedia()) {
397  NS_ENSURE_SUCCESS(rv, rv);
398 
399  // Since the user has turned off the media exporting prefs, tell the
400  // export-agent to unregister itself. This call will remove any
401  // startup/login hooks that the agent has setup.
402  nsCOMPtr<sbIMediaExportAgentService> agentService =
403  do_GetService(SB_MEDIAEXPORTAGENTSERVICE_CONTRACTID, &rv);
404  if (NS_SUCCEEDED(rv) && agentService) {
405  rv = agentService->UnregisterExportAgent();
406  NS_ENSURE_SUCCESS(rv, rv);
407  }
408  }
409 
410  return NS_OK;
411 }
412 
413 nsresult
415 {
416  NS_ENSURE_ARG_POINTER(aMediaList);
417 
418  nsresult rv;
419 
420 #ifdef PR_LOGGING
421  nsString listGuid;
422  rv = aMediaList->GetGuid(listGuid);
423  NS_ENSURE_SUCCESS(rv, rv);
424  LOG("%s: Adding listener for media list '%s",
425  __FUNCTION__, NS_ConvertUTF16toUTF8(listGuid).get());
426 #endif
427 
428  nsString listType;
429  rv = aMediaList->GetType(listType);
430  NS_ENSURE_SUCCESS(rv, rv);
431 
432  if (listType.EqualsLiteral("smart")) {
433  // Listen to rebuild notices only for smart playlists.
434  nsCOMPtr<sbILocalDatabaseSmartMediaList> smartList =
435  do_QueryInterface(aMediaList, &rv);
436  NS_ENSURE_SUCCESS(rv, rv);
437 
438  rv = smartList->AddSmartMediaListListener(this);
439  NS_ENSURE_SUCCESS(rv, rv);
440 
441  NS_ENSURE_TRUE(mObservedSmartMediaLists.AppendObject(smartList),
442  NS_ERROR_FAILURE);
443  }
444  else {
445  // Use the regular medialist listener hook for regular playlists.
446  if (!mFilteredProperties) {
447  mFilteredProperties =
448  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
449  NS_ENSURE_SUCCESS(rv, rv);
450 
451  rv = mFilteredProperties->SetStrict(PR_FALSE);
452  NS_ENSURE_SUCCESS(rv, rv);
453 
454  // Content URL
455  rv = mFilteredProperties->AppendProperty(
456  NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
457  EmptyString());
458  NS_ENSURE_SUCCESS(rv, rv);
459 
460  // GUID
461  rv = mFilteredProperties->AppendProperty(
462  NS_LITERAL_STRING(SB_PROPERTY_GUID),
463  EmptyString());
464  NS_ENSURE_SUCCESS(rv, rv);
465 
466  // Playlist Name
467  rv = mFilteredProperties->AppendProperty(
468  NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME),
469  EmptyString());
470  NS_ENSURE_SUCCESS(rv, rv);
471  }
472 
477 
478  rv = aMediaList->AddListener(this, PR_FALSE, flags, nsnull);
479  NS_ENSURE_SUCCESS(rv, rv);
480 
481  NS_ENSURE_TRUE(mObservedMediaLists.AppendObject(aMediaList),
482  NS_ERROR_FAILURE);
483  }
484 
485  return NS_OK;
486 }
487 
488 nsresult
490  PRBool *aShouldWatch)
491 {
492  NS_ENSURE_ARG_POINTER(aMediaList);
493  NS_ENSURE_ARG_POINTER(aShouldWatch);
494 
495  *aShouldWatch = PR_FALSE;
496  nsresult rv;
497 
498 #ifdef PR_LOGGING
499  nsString mediaListName;
500  rv = aMediaList->GetName(mediaListName);
501  NS_ENSURE_SUCCESS(rv, rv);
502  LOG("%s: Deciding if medialist '%s' should be watched.",
503  __FUNCTION__, NS_ConvertUTF16toUTF8(mediaListName).get());
504 #endif
505 
506  nsString propValue;
507 
508  // Don't watch the Downloads folder
509  rv = aMediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CUSTOMTYPE),
510  propValue);
511  if (NS_FAILED(rv) || propValue.EqualsLiteral("download")) {
512  return NS_OK;
513  }
514 
515  // Don't watch playlists that are owned by iTunes
516  rv = aMediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
517  propValue);
518  if (NS_SUCCEEDED(rv) && !propValue.IsEmpty()) {
519  return NS_OK;
520  }
521 
522  // Don't watch subscriptions
523  rv = aMediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISSUBSCRIPTION),
524  propValue);
525  if (NS_FAILED(rv) || propValue.EqualsLiteral("1")) {
526  return NS_OK;
527  }
528 
529  // Make sure this list isn't supposed to be hidden
530  rv = aMediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
531  propValue);
532  if (NS_FAILED(rv) || propValue.EqualsLiteral("1")) {
533  return NS_OK;
534  }
535 
536  // Filter out the users choice for playlist exporting
537  rv = aMediaList->GetType(propValue);
538  NS_ENSURE_SUCCESS(rv, rv);
539 
540  if (propValue.EqualsLiteral("simple") &&
541  !mPrefController->GetShouldExportPlaylists())
542  {
543  return NS_OK;
544  }
545 
546  if (propValue.EqualsLiteral("smart") &&
547  !mPrefController->GetShouldExportSmartPlaylists())
548  {
549  return NS_OK;
550  }
551 
552  // Passed all tests, this is a playlist that should be watched
553  *aShouldWatch = PR_TRUE;
554  return NS_OK;
555 }
556 
557 void
559 {
561  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
562  "ERROR: Could not begin exporting songbird data!");
563 }
564 
565 void
567 {
568  nsresult rv;
569  rv = NotifyListeners();
570  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
571  "ERROR: Could not notify the listeners!");
572 }
573 
574 nsresult
576 {
577  LOG("%s: Writing recorded media changes to task file", __FUNCTION__);
578  nsresult rv;
579 
580  if (GetHasRecordedChanges()) {
581  LOG("%s: Starting to export data", __FUNCTION__);
582 
583  // The order of exported data looks like this
584  // 1.) Added media lists
585  // 2.) Removed media lists
586  // 3.) Medialist added items
587 
588  mTaskWriter = new sbMediaExportTaskWriter();
589  NS_ENSURE_TRUE(mTaskWriter, NS_ERROR_OUT_OF_MEMORY);
590 
591  rv = mTaskWriter->Init();
592  NS_ENSURE_SUCCESS(rv, rv);
593 
594  // Export media lists first
595  if (mPrefController->GetShouldExportAnyPlaylists()) {
596  rv = WriteAddedMediaLists();
597  NS_ENSURE_SUCCESS(rv, rv);
598 
599  rv = NotifyListeners();
600  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify listeners!");
601 
602  rv = WriteRemovedMediaLists();
603  NS_ENSURE_SUCCESS(rv, rv);
604 
605  rv = NotifyListeners();
606  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify listeners!");
607 
608  if (mPrefController->GetShouldExportSmartPlaylists()) {
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  rv = NotifyListeners();
613  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify listeners!");
614  }
615  }
616 
617  // Export media items next
618  if (mPrefController->GetShouldExportTracks()) {
619  rv = WriteAddedMediaItems();
620  NS_ENSURE_SUCCESS(rv, rv);
621  rv = WriteUpdatedMediaItems();
622  NS_ENSURE_SUCCESS(rv, rv);
623  }
624 
625  rv = NotifyListeners();
626  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify listeners!");
627 
628  rv = mTaskWriter->Finish();
629  NS_ENSURE_SUCCESS(rv, rv);
630  } // end should write data.
631 
632  LOG("%s: Done exporting data", __FUNCTION__);
633 
634  mAddedMediaList.clear();
635  mAddedItemsMap.clear();
636  mRemovedMediaLists.clear();
637 
639  rv = NotifyListeners();
640  NS_ENSURE_SUCCESS(rv, rv);
641 
642  // Only start the export agent if we are supposed to.
643  if (mPrefController->GetShouldStartExportAgent()) {
644  nsCOMPtr<sbIMediaExportAgentService> agentService =
645  do_GetService(SB_MEDIAEXPORTAGENTSERVICE_CONTRACTID, &rv);
646  if (NS_SUCCEEDED(rv) && agentService) {
647  rv = agentService->StartExportAgent();
648  NS_ENSURE_SUCCESS(rv, rv);
649  }
650  }
651 
652  return NS_OK;
653 }
654 
655 nsresult
657 {
658  if (mAddedMediaList.size() == 0) {
659  return NS_OK;
660  }
661 
662  LOG("%s: Writing added media lists", __FUNCTION__);
663 
664  NS_ENSURE_TRUE(mTaskWriter, NS_ERROR_UNEXPECTED);
665 
666  nsresult rv;
667  rv = mTaskWriter->WriteAddedMediaListsHeader();
668  NS_ENSURE_SUCCESS(rv, rv);
669 
670  sbStringListIter begin = mAddedMediaList.begin();
671  sbStringListIter end = mAddedMediaList.end();
673 
674  for (next = begin; next != end; ++next) {
675  nsCOMPtr<sbIMediaList> curMediaList;
676  rv = GetMediaListByGuid(*next, getter_AddRefs(curMediaList));
677  if (NS_FAILED(rv) || !curMediaList) {
678  NS_WARNING("ERROR: Could not get a media list by GUID!");
679  continue;
680  }
681 
682  rv = mTaskWriter->WriteMediaListName(curMediaList);
683  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
684  "ERROR: Could not write the added media list name!");
685 
686  ++mProgress;
687  }
688 
689  return NS_OK;
690 }
691 
692 nsresult
694 {
695  if (mRemovedMediaLists.size() == 0) {
696  return NS_OK;
697  }
698 
699  LOG("%s: Writing removed media lists", __FUNCTION__);
700 
701  NS_ENSURE_TRUE(mTaskWriter, NS_ERROR_UNEXPECTED);
702 
703  nsresult rv;
704  rv = mTaskWriter->WriteRemovedMediaListsHeader();
705  NS_ENSURE_SUCCESS(rv, rv);
706 
707  sbStringListIter begin = mRemovedMediaLists.begin();
708  sbStringListIter end = mRemovedMediaLists.end();
710 
711  for (next = begin; next != end; ++next) {
712  rv = mTaskWriter->WriteEscapedString(*next);
713  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
714  "ERROR: Could not write the removed media list name!");
715 
716  ++mProgress;
717  }
718 
719  return NS_OK;
720 }
721 
722 nsresult
724 {
725  if (mAddedItemsMap.size() == 0) {
726  return NS_OK;
727  }
728 
729  LOG("%s: Writing %i media lists with added media items",
730  __FUNCTION__, mAddedItemsMap.size());
731 
732  NS_ENSURE_TRUE(mTaskWriter, NS_ERROR_UNEXPECTED);
733 
734  nsresult rv;
735  nsCOMPtr<sbILibrary> mainLibrary;
736  rv = GetMainLibrary(getter_AddRefs(mainLibrary));
737  NS_ENSURE_SUCCESS(rv, rv);
738 
739  nsString mainLibraryGuid;
740  rv = mainLibrary->GetGuid(mainLibraryGuid);
741  NS_ENSURE_SUCCESS(rv, rv);
742 
743  sbMediaListItemMapIter begin = mAddedItemsMap.begin();
744  sbMediaListItemMapIter end = mAddedItemsMap.end();
746 
747  for (next = begin; next != end; ++next) {
748  nsString curMediaListGuid(next->first);
749  nsCOMPtr<sbIMediaList> curParentList;
750  rv = GetMediaListByGuid(curMediaListGuid, getter_AddRefs(curParentList));
751  if (NS_FAILED(rv) || !curParentList) {
752  NS_WARNING("ERROR: Could not look up a media list by guid!");
753  continue;
754  }
755 
756  // pass the flag if this list is the main Songbird library
757  if (mainLibraryGuid.Equals(curMediaListGuid)) {
758  rv = mTaskWriter->WriteAddedMediaItemsListHeader(curParentList, PR_TRUE);
759  } else {
760  rv = mTaskWriter->WriteAddedMediaItemsListHeader(curParentList);
761  }
762  if (NS_FAILED(rv)) {
763  NS_WARNING("ERROR: Could not write the media item list header!");
764  continue;
765  }
766 
767  nsCOMPtr<nsIArray> addedMediaItems;
768  rv = EnumerateItemsByGuids<sbStringList>(next->second.begin(),
769  next->second.end(),
770  curParentList,
771  getter_AddRefs(addedMediaItems));
772  if (NS_FAILED(rv) || !addedMediaItems) {
773  NS_WARNING("ERROR: Could not enumerate media items by guid!");
774  continue;
775  }
776 
777  // Print out all the tracks in the media list.
778  rv = WriteMediaItemsArray(addedMediaItems);
779  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
780  "ERROR: Could not write out the media items!");
781  }
782 
783  return NS_OK;
784 }
785 
786 nsresult
788 {
789  TRACE("%s: Writing %i updated media items",
790  __FUNCTION__,
791  mUpdatedItems.size());
792  if (mUpdatedItems.empty()) {
793  return NS_OK;
794  }
795 
796  NS_ENSURE_STATE(mTaskWriter);
797 
798  nsresult rv;
799 
800  nsCOMPtr<sbILibrary> mainLibrary;
801  rv = GetMainLibrary(getter_AddRefs(mainLibrary));
802  NS_ENSURE_SUCCESS(rv, rv);
803 
804  nsCOMPtr<nsIArray> updatedMediaItems;
805  rv = EnumerateItemsByGuids<sbStringSet>(mUpdatedItems.begin(),
806  mUpdatedItems.end(),
807  mainLibrary,
808  getter_AddRefs(updatedMediaItems));
809  if (NS_FAILED(rv) || !updatedMediaItems) {
810  NS_WARNING("ERROR: Could not enumerate media items by guid!");
811  return NS_ERROR_FAILURE;;
812  }
813 
814  rv = mTaskWriter->WriteUpdatedMediaItemsListHeader();
815  NS_ENSURE_SUCCESS(rv, rv);
816 
817  PRUint32 length = 0;
818  rv = updatedMediaItems->GetLength(&length);
819  NS_ENSURE_SUCCESS(rv, rv);
820 
821  PRUint32 updatedtemDelta = 0;
822 
823  for (PRUint32 i = 0; i < length; i++) {
824  nsCOMPtr<sbIMediaItem> curMediaItem =
825  do_QueryElementAt(updatedMediaItems, i, &rv);
826  if (NS_FAILED(rv) || !curMediaItem) {
827  NS_WARNING("ERROR: Could not get a mediaitem from an array!");
828  continue;
829  }
830 
831  rv = mTaskWriter->WriteUpdatedTrack(curMediaItem);
832  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
833  "ERROR: Could not write a smartlist mediaitem!");
834 
835  ++mProgress;
836  ++updatedtemDelta;
837 
838  // Go ahead and notify the listener after each write.
839  if (updatedtemDelta == LISTENER_NOTIFY_ITEM_DELTA) {
840  rv = NotifyListeners();
841  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
842  "ERROR: Could not notify the listeners!");
843  updatedtemDelta = 0;
844  }
845  }
846 
847  if (updatedtemDelta > 0) {
848  rv = NotifyListeners();
849  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
850  "ERROR: Could not notify the listeners!");
851  }
852 
853  return NS_OK;
854 }
855 
856 nsresult
858 {
859  if (mUpdatedSmartMediaLists.size() == 0) {
860  return NS_OK;
861  }
862 
863  LOG("%s: Writing updated smart playlists", __FUNCTION__);
864 
865  NS_ENSURE_TRUE(mTaskWriter, NS_ERROR_UNEXPECTED);
866 
867  nsresult rv;
868  sbStringListIter begin = mUpdatedSmartMediaLists.begin();
869  sbStringListIter end = mUpdatedSmartMediaLists.end();
871  for (next = begin; next != end; ++next) {
872  // Get the smart playlist as a medialist to write the update header with
873  // the task writer. NOTE: This guid is always going to be a smart list guid.
874  nsCOMPtr<sbIMediaList> curSmartMediaList;
875  rv = GetMediaListByGuid(*next, getter_AddRefs(curSmartMediaList));
876  if (NS_FAILED(rv) || !curSmartMediaList) {
877  NS_WARNING("ERROR: Could not get a smart list by GUID!");
878  continue;
879  }
880 
881  nsRefPtr<sbMediaListEnumArrayHelper> enumHelper =
883  NS_ENSURE_TRUE(enumHelper, NS_ERROR_OUT_OF_MEMORY);
884 
885  rv = curSmartMediaList->EnumerateAllItems(
886  enumHelper,
888  NS_ENSURE_SUCCESS(rv, rv);
889 
890  nsCOMPtr<nsIArray> mediaItems;
891  rv = enumHelper->GetMediaItemsArray(getter_AddRefs(mediaItems));
892  NS_ENSURE_SUCCESS(rv, rv);
893 
894  PRUint32 length = 0;
895  rv = mediaItems->GetLength(&length);
896  NS_ENSURE_SUCCESS(rv, rv);
897 
898  if (length == 0) {
899  continue;
900  }
901 
902  rv = mTaskWriter->WriteUpdatedSmartPlaylistHeader(curSmartMediaList);
903  if (NS_FAILED(rv)) {
904  NS_WARNING("ERROR: Could not write the updated smartlist header!");
905  continue;
906  }
907 
908  // Print out all the tracks in the media list.
909  rv = WriteMediaItemsArray(mediaItems);
910  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
911  "ERROR: Could not write out the media items!");
912  }
913 
914  return NS_OK;
915 }
916 
917 nsresult
919 {
920  NS_ENSURE_ARG_POINTER(aItemsArray);
921 
922  PRUint32 length = 0;
923  nsresult rv = aItemsArray->GetLength(&length);
924  NS_ENSURE_SUCCESS(rv, rv);
925 
926  PRUint32 addedItemDelta = 0;
927 
928  for (PRUint32 i = 0; i < length; i++) {
929  nsCOMPtr<sbIMediaItem> curMediaItem =
930  do_QueryElementAt(aItemsArray, i, &rv);
931  if (NS_FAILED(rv) || !curMediaItem) {
932  NS_WARNING("ERROR: Could not get a mediaitem from an array!");
933  continue;
934  }
935 
936  rv = mTaskWriter->WriteAddedTrack(curMediaItem);
937  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
938  "ERROR: Could not write a smartlist mediaitem!");
939 
940  ++mProgress;
941  ++addedItemDelta;
942 
943  // Go ahead and notify the listener after each write.
944  if (addedItemDelta == LISTENER_NOTIFY_ITEM_DELTA) {
945  rv = NotifyListeners();
946  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
947  "ERROR: Could not notify the listeners!");
948  addedItemDelta = 0;
949  }
950  }
951 
952  if (addedItemDelta > 0) {
953  rv = NotifyListeners();
954  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
955  "ERROR: Could not notify the listeners!");
956  }
957 
958  return NS_OK;
959 }
960 
961 nsresult
962 sbMediaExportService::GetMediaListByGuid(const nsAString & aItemGuid,
963  sbIMediaList **aMediaList)
964 {
965  LOG("%s: Getting item by GUID '%s'",
966  __FUNCTION__, NS_ConvertUTF16toUTF8(aItemGuid).get());
967 
968  nsresult rv;
969  nsCOMPtr<sbILibrary> mainLibrary;
970  rv = GetMainLibrary(getter_AddRefs(mainLibrary));
971  NS_ENSURE_SUCCESS(rv, rv);
972 
973  nsString mainLibraryGuid;
974  rv = mainLibrary->GetGuid(mainLibraryGuid);
975  NS_ENSURE_SUCCESS(rv, rv);
976 
977  nsCOMPtr<sbIMediaList> itemAsList;
978 
979  // If this is the main library GUID, return that.
980  if (mainLibraryGuid.Equals(aItemGuid)) {
981  itemAsList = do_QueryInterface(mainLibrary, &rv);
982  NS_ENSURE_SUCCESS(rv, rv);
983  }
984  else {
985  nsCOMPtr<sbIMediaItem> mediaItem;
986  rv = mainLibrary->GetItemByGuid(aItemGuid, getter_AddRefs(mediaItem));
987  NS_ENSURE_SUCCESS(rv, rv);
988 
989  itemAsList = do_QueryInterface(mediaItem, &rv);
990  NS_ENSURE_SUCCESS(rv, rv);
991  }
992 
993  itemAsList.swap(*aMediaList);
994  return NS_OK;
995 }
996 
997 nsresult
999 {
1000  LOG("%s: updating listeners of job progress", __FUNCTION__);
1001 
1002  nsresult rv;
1003  if (!NS_IsMainThread()) {
1004  // Since all of the listeners expect to be notified on the main thread,
1005  // proxy back to this method on the main thread.
1006  nsCOMPtr<nsIThread> mainThread;
1007  rv = NS_GetMainThread(getter_AddRefs(mainThread));
1008  NS_ENSURE_SUCCESS(rv, rv);
1009 
1010  nsCOMPtr<nsIRunnable> runnable =
1011  NS_NEW_RUNNABLE_METHOD(sbMediaExportService, this, ProxyNotifyListeners);
1012  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
1013 
1014  return mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
1015  }
1016 
1017  for (PRInt32 i = 0; i < mJobListeners.Count(); i++) {
1018  rv = mJobListeners[i]->OnJobProgress(this);
1019  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1020  "Could not notify job progress listener!");
1021  }
1022 
1023  return NS_OK;
1024 }
1025 
1026 PRBool
1028 {
1029  return !mAddedItemsMap.empty() ||
1030  !mUpdatedItems.empty() ||
1031  !mAddedMediaList.empty() ||
1032  !mRemovedMediaLists.empty() ||
1033  !mUpdatedSmartMediaLists.empty();
1034 }
1035 
1036 //------------------------------------------------------------------------------
1037 // sbIMediaExportService
1038 
1039 NS_IMETHODIMP
1040 sbMediaExportService::GetHasPendingChanges(PRBool *aHasPendingChanges)
1041 {
1042  NS_ENSURE_ARG_POINTER(aHasPendingChanges);
1043  *aHasPendingChanges = GetHasRecordedChanges();
1044 
1045  LOG("%s: Media-export service has pending changes == %s",
1046  __FUNCTION__, (*aHasPendingChanges ? "true" : "false"));
1047 
1048  return NS_OK;
1049 }
1050 
1051 NS_IMETHODIMP
1052 sbMediaExportService::ExportSongbirdData()
1053 {
1054  LOG("%s: Exporting songbird data (manual trigger)", __FUNCTION__);
1055 
1056  nsresult rv;
1058  rv = NotifyListeners();
1059  NS_ENSURE_SUCCESS(rv, rv);
1060 
1061  // Since looking up the stashed data locks the main thread, write out the
1062  // export data on a background thread.
1063  nsCOMPtr<nsIThreadPool> threadPoolService =
1064  do_GetService(SB_THREADPOOLSERVICE_CONTRACTID, &rv);
1065  NS_ENSURE_SUCCESS(rv, rv);
1066 
1067  nsCOMPtr<nsIRunnable> runnable =
1068  NS_NEW_RUNNABLE_METHOD(sbMediaExportService, this, WriteExportData);
1069  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
1070 
1071  return threadPoolService->Dispatch(runnable, NS_DISPATCH_NORMAL);
1072 }
1073 
1074 //------------------------------------------------------------------------------
1075 // nsIObserver
1076 
1077 NS_IMETHODIMP
1078 sbMediaExportService::Observe(nsISupports *aSubject,
1079  const char *aTopic,
1080  const PRUnichar *aData)
1081 {
1082  NS_ENSURE_ARG_POINTER(aTopic);
1083 
1084  LOG("%s: Topic '%s' observed", __FUNCTION__, aTopic);
1085 
1086  nsresult rv;
1087  if (strcmp(aTopic, SB_LIBRARY_MANAGER_READY_TOPIC) == 0) {
1088  // No need to listen for the library manager ready notice
1089  nsCOMPtr<nsIObserverService> observerService =
1090  do_GetService("@mozilla.org/observer-service;1", &rv);
1091  NS_ENSURE_SUCCESS(rv, rv);
1092 
1093  rv = observerService->RemoveObserver(this, SB_LIBRARY_MANAGER_READY_TOPIC);
1094  NS_ENSURE_SUCCESS(rv, rv);
1095 
1096  rv = InitInternal();
1097  NS_ENSURE_SUCCESS(rv, rv);
1098  }
1099  else if (strcmp(aTopic, SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC) == 0) {
1100  rv = Shutdown();
1101  NS_ENSURE_SUCCESS(rv, rv);
1102  }
1103 
1104  return NS_OK;
1105 }
1106 
1107 //------------------------------------------------------------------------------
1108 // sbIMediaListListener
1109 
1110 NS_IMETHODIMP
1111 sbMediaExportService::OnItemAdded(sbIMediaList *aMediaList,
1112  sbIMediaItem *aMediaItem,
1113  PRUint32 aIndex,
1114  PRBool *aRetVal)
1115 {
1116  LOG("%s: Media Item Added!", __FUNCTION__);
1117 
1118  NS_ENSURE_ARG_POINTER(aMediaList);
1119  NS_ENSURE_ARG_POINTER(aMediaItem);
1120 
1121  nsresult rv;
1122 
1123  nsString itemGuid;
1124  rv = aMediaItem->GetGuid(itemGuid);
1125  NS_ENSURE_SUCCESS(rv, rv);
1126 
1127  // Handle the mediaitem if it is a playlist (and exporting for lists is on).
1128  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
1129  if (itemAsList && NS_SUCCEEDED(rv)) {
1130  if (mPrefController->GetShouldExportPlaylists() ||
1131  mPrefController->GetShouldExportSmartPlaylists())
1132  {
1133  // Only worry if this is a list that we should be watching
1134  PRBool shouldWatchList = PR_FALSE;
1135  rv = GetShouldWatchMediaList(itemAsList, &shouldWatchList);
1136  if (NS_SUCCEEDED(rv) && shouldWatchList) {
1137  rv = ListenToMediaList(itemAsList);
1138  NS_ENSURE_SUCCESS(rv, rv);
1139 
1140  mAddedMediaList.push_back(itemGuid);
1141  }
1142  }
1143  }
1144  // Handle the added mediaitem track.
1145  else {
1146  nsCOMPtr<sbILibrary> mainLibrary;
1147  rv = GetMainLibrary(getter_AddRefs(mainLibrary));
1148  NS_ENSURE_SUCCESS(rv, rv);
1149  if (SameCOMIdentity(mainLibrary, aMediaList)) {
1150  // this item was added to the main library; check if it already has an
1151  // itunes persistent id, and ignore it if so (because that means we just
1152  // imported the track from itunes)
1153  nsString itunesGuid;
1154  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
1155  itunesGuid);
1156  if (NS_SUCCEEDED(rv) && !itunesGuid.IsEmpty()) {
1157  // this item already exists
1158  return NS_OK;
1159  }
1160  }
1161 
1162  nsString listGuid;
1163  rv = aMediaList->GetGuid(listGuid);
1164  NS_ENSURE_SUCCESS(rv, rv);
1165 
1166  // Add this mediaitem's guid to the parent medialist's guid string set
1167  // in the added items map.
1168  sbMediaListItemMapIter guidIter = mAddedItemsMap.find(listGuid);
1169  if (guidIter == mAddedItemsMap.end()) {
1170  // The media list isn't represented in the map, add a new entry for this
1171  // media list using its guid.
1172  std::pair<sbMediaListItemMapIter, bool> insertedPair =
1173  mAddedItemsMap.insert(sbMediaListItemMapPair(listGuid, sbStringList()));
1174 
1175  NS_ENSURE_TRUE(insertedPair.second, NS_ERROR_FAILURE);
1176  guidIter = insertedPair.first;
1177  }
1178 
1179  guidIter->second.push_back(itemGuid);
1180  }
1181 
1182  return NS_OK;
1183 }
1184 
1185 NS_IMETHODIMP
1186 sbMediaExportService::OnBeforeItemRemoved(sbIMediaList *aMediaList,
1187  sbIMediaItem *aMediaItem,
1188  PRUint32 aIndex,
1189  PRBool *aRetVal)
1190 {
1191  *aRetVal = PR_TRUE;
1192  return NS_OK;
1193 }
1194 
1195 NS_IMETHODIMP
1196 sbMediaExportService::OnAfterItemRemoved(sbIMediaList *aMediaList,
1197  sbIMediaItem *aMediaItem,
1198  PRUint32 aIndex, PRBool *_retval)
1199 {
1200  LOG("%s: After Media Item Removed!!", __FUNCTION__);
1201 
1202  if (mPrefController->GetShouldExportPlaylists() ||
1203  mPrefController->GetShouldExportSmartPlaylists())
1204  {
1205  nsresult rv;
1206  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
1207  if (NS_SUCCEEDED(rv) && itemAsList) {
1208  // If this is a list that is currently being observed by this service,
1209  // then remove the listener hook and add the list name to the removed
1210  // media lists string list.
1211 
1212  nsString type;
1213  rv = itemAsList->GetType(type);
1214  NS_ENSURE_SUCCESS(rv, rv);
1215 
1216  if (type.EqualsLiteral("smart")) {
1217  nsCOMPtr<sbILocalDatabaseSmartMediaList> itemAsSmartList =
1218  do_QueryInterface(itemAsList, &rv);
1219  NS_ENSURE_SUCCESS(rv, rv);
1220 
1221  PRInt32 index = mObservedSmartMediaLists.IndexOf(itemAsSmartList);
1222  if (index > -1) {
1223  nsString listName;
1224  rv = itemAsList->GetName(listName);
1225  NS_ENSURE_SUCCESS(rv, rv);
1226 
1227  rv = itemAsSmartList->RemoveSmartMediaListListener(this);
1228  NS_ENSURE_SUCCESS(rv, rv);
1229 
1230  mRemovedMediaLists.push_back(listName);
1231  }
1232  }
1233  else {
1234  PRInt32 index = mObservedMediaLists.IndexOf(itemAsList);
1235  if (index > -1) {
1236  nsString listName;
1237  rv = itemAsList->GetName(listName);
1238  NS_ENSURE_SUCCESS(rv, rv);
1239 
1240  mRemovedMediaLists.push_back(listName);
1241 
1242  rv = itemAsList->RemoveListener(this);
1243  NS_ENSURE_SUCCESS(rv, rv);
1244 
1245  NS_WARN_IF_FALSE(mObservedMediaLists.RemoveObjectAt(index),
1246  "Could not remove the media list from the observed lists array!");
1247  }
1248  }
1249  }
1250  }
1251 
1252  return NS_OK;
1253 }
1254 
1255 NS_IMETHODIMP
1256 sbMediaExportService::OnItemUpdated(sbIMediaList *aMediaList,
1257  sbIMediaItem *aMediaItem,
1258  sbIPropertyArray *aProperties,
1259  PRBool *aRetVal)
1260 {
1261  LOG("%s: Media Item Updated!!", __FUNCTION__);
1262 
1263  NS_ENSURE_ARG_POINTER(aMediaList);
1264  NS_ENSURE_ARG_POINTER(aMediaItem);
1265  NS_ENSURE_ARG_POINTER(aProperties);
1266  NS_ENSURE_ARG_POINTER(aRetVal);
1267 
1268  // we always want more notifications
1269  *aRetVal = PR_FALSE;
1270 
1271  nsresult rv;
1272  #if DEBUG
1273  {
1274  nsCOMPtr<sbILibrary> mainLib;
1275  rv = GetMainLibrary(getter_AddRefs(mainLib));
1276  NS_ENSURE_SUCCESS(rv, rv);
1277  NS_ASSERTION(SameCOMIdentity(mainLib, aMediaList),
1278  "sbMediaExportService::OnItemUpdated: unexpected media list");
1279  }
1280  #endif
1281 
1282  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
1283  if (itemAsList && NS_SUCCEEDED(rv)) {
1284  // this is a media list; we don't care about those
1285  return NS_OK;
1286  }
1287 
1288  nsString contentUrl;
1289  rv = aProperties->GetPropertyValue(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
1290  contentUrl);
1291  if (NS_FAILED(rv)) {
1292  // the content url didn't change, something else did
1293  // we don't need to update the export
1294  return NS_OK;
1295  }
1296 
1297  nsString itunesGuid;
1298  rv = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ITUNES_GUID),
1299  itunesGuid);
1300  if (NS_FAILED(rv)) {
1301  // no itunes guid, new item?
1302  NS_NOTREACHED("not implemented");
1303  return NS_ERROR_NOT_IMPLEMENTED;
1304  }
1305 
1306  nsString itemGuid;
1307  rv = aMediaItem->GetGuid(itemGuid);
1308  NS_ENSURE_SUCCESS(rv, rv);
1309 
1310  // Add this item's guid to the list of things that were changed
1311  mUpdatedItems.insert(itemGuid);
1312 
1313  return NS_OK;
1314 }
1315 
1316 NS_IMETHODIMP
1317 sbMediaExportService::OnItemMoved(sbIMediaList *aMediaList,
1318  PRUint32 aFromIndex,
1319  PRUint32 aToIndex,
1320  PRBool *aRetVal)
1321 {
1322  LOG("%s: Media Item Moved!", __FUNCTION__);
1323  return NS_OK;
1324 }
1325 NS_IMETHODIMP
1326 sbMediaExportService::OnBeforeListCleared(sbIMediaList *aMediaList,
1327  PRBool aExcludeLists,
1328  PRBool *aRetVal)
1329 {
1330  LOG("%s: Media List Before Cleared!", __FUNCTION__);
1331  return NS_OK;
1332 }
1333 
1334 NS_IMETHODIMP
1335 sbMediaExportService::OnListCleared(sbIMediaList *aMediaList,
1336  PRBool aExcludeLists,
1337  PRBool *aRetVal)
1338 {
1339  LOG("%s: Media List Cleared!", __FUNCTION__);
1340  return NS_OK;
1341 }
1342 
1343 NS_IMETHODIMP
1344 sbMediaExportService::OnBatchBegin(sbIMediaList *aMediaList)
1345 {
1346  LOG("%s: Media List Batch Begin!", __FUNCTION__);
1347  return NS_OK;
1348 }
1349 
1350 NS_IMETHODIMP
1351 sbMediaExportService::OnBatchEnd(sbIMediaList *aMediaList)
1352 {
1353  LOG("%s: Media List Batch End!", __FUNCTION__);
1354  return NS_OK;
1355 }
1356 
1357 //------------------------------------------------------------------------------
1358 // sbILocalDatabaseSmartMediaListListener
1359 
1360 NS_IMETHODIMP
1361 sbMediaExportService::OnRebuild(sbILocalDatabaseSmartMediaList *aSmartMediaList)
1362 {
1363  NS_ENSURE_ARG_POINTER(aSmartMediaList);
1364 
1365  nsresult rv;
1366  nsString listGuid;
1367  rv = aSmartMediaList->GetGuid(listGuid);
1368  NS_ENSURE_SUCCESS(rv, rv);
1369 
1370  LOG("%s: Observing updated smart media list for '%s'",
1371  __FUNCTION__, NS_ConvertUTF16toUTF8(listGuid).get());
1372 
1373  sbStringListIter result = std::find(mUpdatedSmartMediaLists.begin(),
1374  mUpdatedSmartMediaLists.end(),
1375  listGuid);
1376  if (result == mUpdatedSmartMediaLists.end()) {
1377  mUpdatedSmartMediaLists.push_back(listGuid);
1378  }
1379 
1380  return NS_OK;
1381 }
1382 
1383 //------------------------------------------------------------------------------
1384 // sbIJobProgress
1385 
1386 NS_IMETHODIMP
1387 sbMediaExportService::GetStatus(PRUint16 *aStatus)
1388 {
1389  NS_ENSURE_ARG_POINTER(aStatus);
1390  *aStatus = mStatus;
1391  return NS_OK;
1392 }
1393 
1394 NS_IMETHODIMP
1395 sbMediaExportService::GetBlocked(PRBool *aBlocked)
1396 {
1397  NS_ENSURE_ARG_POINTER(aBlocked);
1398  *aBlocked = PR_FALSE;
1399  return NS_OK;
1400 }
1401 
1402 NS_IMETHODIMP
1403 sbMediaExportService::GetStatusText(nsAString & aStatusText)
1404 {
1405  return SBGetLocalizedString(aStatusText,
1406  NS_LITERAL_STRING("mediaexport.shutdowntaskname"));
1407 }
1408 
1409 NS_IMETHODIMP
1410 sbMediaExportService::GetTitleText(nsAString & aTitleText)
1411 {
1412  // Not used by the shutdown service
1413  return NS_OK;
1414 }
1415 
1416 NS_IMETHODIMP
1417 sbMediaExportService::GetProgress(PRUint32 *aProgress)
1418 {
1419  NS_ENSURE_ARG_POINTER(aProgress);
1420  *aProgress = mProgress;
1421  return NS_OK;
1422 }
1423 
1424 NS_IMETHODIMP
1425 sbMediaExportService::GetTotal(PRUint32 *aTotal)
1426 {
1427  NS_ENSURE_ARG_POINTER(aTotal);
1428  *aTotal = mTotal;
1429  return NS_OK;
1430 }
1431 
1432 NS_IMETHODIMP
1433 sbMediaExportService::GetErrorCount(PRUint32 *aErrorCount)
1434 {
1435  NS_ENSURE_ARG_POINTER(aErrorCount);
1436  // Not used by the shutdown service
1437  *aErrorCount = 0;
1438  return NS_OK;
1439 }
1440 
1441 NS_IMETHODIMP
1442 sbMediaExportService::GetErrorMessages(nsIStringEnumerator **aRetVal)
1443 {
1444  NS_ENSURE_ARG_POINTER(aRetVal);
1445  // Not used by the shutdown service
1446  *aRetVal = nsnull;
1447  return NS_OK;
1448 }
1449 
1450 NS_IMETHODIMP
1451 sbMediaExportService::AddJobProgressListener(sbIJobProgressListener *aListener)
1452 {
1453  NS_ENSURE_ARG_POINTER(aListener);
1454  NS_ENSURE_TRUE(mJobListeners.AppendObject(aListener), NS_ERROR_FAILURE);
1455  return NS_OK;
1456 }
1457 
1458 NS_IMETHODIMP
1459 sbMediaExportService::RemoveJobProgressListener(sbIJobProgressListener *aListener)
1460 {
1461  NS_ENSURE_ARG_POINTER(aListener);
1462  NS_WARN_IF_FALSE(mJobListeners.RemoveObject(aListener),
1463  "Could not remove the job progress listener!");
1464  return NS_OK;
1465 }
1466 
1467 //------------------------------------------------------------------------------
1468 // sbIShutdownTask
1469 
1470 NS_IMETHODIMP
1471 sbMediaExportService::GetNeedsToRunTask(PRBool *aNeedsToRunTask)
1472 {
1473  NS_ENSURE_ARG_POINTER(aNeedsToRunTask);
1474 
1475  nsresult rv;
1476  *aNeedsToRunTask = PR_FALSE;
1477 
1478  rv = GetHasPendingChanges(aNeedsToRunTask);
1479  NS_ENSURE_SUCCESS(rv, rv);
1480 
1481  if (!*aNeedsToRunTask && mPrefController->GetShouldExportAnyMedia()) {
1482  // Since there isn't any pending changes, check to see if the agent should
1483  // be started. Only start the agent if it is not running and there is at
1484  // least one pending export task file on disk.
1485  nsCOMPtr<nsIFile> taskFileParentFolder;
1486  rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
1487  getter_AddRefs(taskFileParentFolder));
1488  NS_ENSURE_SUCCESS(rv, rv);
1489 
1490  // Loop through this folders children to see if we can find a task file
1491  // who's name matches the export task filename.
1492  nsCOMPtr<nsISimpleEnumerator> dirEnum;
1493  rv = taskFileParentFolder->GetDirectoryEntries(getter_AddRefs(dirEnum));
1494  NS_ENSURE_SUCCESS(rv, rv);
1495 
1496  PRBool hasTaskFile = PR_FALSE;
1497  PRBool hasMore = PR_FALSE;
1498  while ((NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore))) && hasMore) {
1499  nsCOMPtr<nsISupports> curItem;
1500  rv = dirEnum->GetNext(getter_AddRefs(curItem));
1501  if (NS_FAILED(rv)) {
1502  continue;
1503  }
1504 
1505  nsCOMPtr<nsIFile> curFile = do_QueryInterface(curItem, &rv);
1506  if (NS_FAILED(rv) || !curFile) {
1507  continue;
1508  }
1509 
1510  nsString curFileName;
1511  rv = curFile->GetLeafName(curFileName);
1512  if (NS_FAILED(rv)) {
1513  continue;
1514  }
1515 
1516  if (curFileName.Equals(NS_LITERAL_STRING(TASKFILE_NAME))) {
1517  hasTaskFile = PR_TRUE;
1518  // Don't bother iterating through the rest of the children.
1519  break;
1520  }
1521  }
1522 
1523  if (hasTaskFile) {
1524  nsCOMPtr<sbIMediaExportAgentService> agentService =
1525  do_GetService(SB_MEDIAEXPORTAGENTSERVICE_CONTRACTID, &rv);
1526  if (NS_SUCCEEDED(rv) && agentService) {
1527  PRBool isAgentRunning = PR_FALSE;
1528  rv = agentService->GetIsAgentRunning(&isAgentRunning);
1529  if (NS_SUCCEEDED(rv) && !isAgentRunning) {
1530  *aNeedsToRunTask = PR_TRUE;
1531  }
1532  }
1533  }
1534  }
1535 
1536  if (*aNeedsToRunTask) {
1537  // Since we are about to run a job, calculate the total item count now.
1538  mProgress = 0;
1539  mTotal = mAddedMediaList.size();
1540  mTotal += mRemovedMediaLists.size();
1541 
1542  sbStringListIter listBegin = mUpdatedSmartMediaLists.begin();
1543  sbStringListIter listEnd = mUpdatedSmartMediaLists.end();
1544  sbStringListIter listNext;
1545  for (listNext = listBegin; listNext != listEnd; ++listNext) {
1546  nsCOMPtr<sbIMediaList> curUpdatedList;
1547  rv = GetMediaListByGuid(*listNext, getter_AddRefs(curUpdatedList));
1548  NS_ENSURE_SUCCESS(rv, rv);
1549 
1550  PRUint32 length;
1551  rv = curUpdatedList->GetLength(&length);
1552  NS_ENSURE_SUCCESS(rv, rv);
1553 
1554  mTotal += length;
1555  }
1556 
1557  sbMediaListItemMapIter begin = mAddedItemsMap.begin();
1558  sbMediaListItemMapIter end = mAddedItemsMap.end();
1560  for (next = begin; next != end; ++next) {
1561  mTotal += next->second.size();
1562  }
1563  }
1564 
1565  LOG("%s: Export service needs to run at shutdown == %s",
1566  __FUNCTION__, (*aNeedsToRunTask ? "true" : "false"));
1567 
1568  return NS_OK;
1569 }
1570 
1571 NS_IMETHODIMP
1572 sbMediaExportService::StartTask()
1573 {
1574  LOG("%s: Starting export service shutdown task...", __FUNCTION__);
1575 
1576  // This method gets called by the shutdown service when it is our turn to
1577  // begin processing. Simply start the export data process here.
1578  return ExportSongbirdData();
1579 }
1580 
1581 //------------------------------------------------------------------------------
1582 // XPCOM Startup Registration
1583 
1584 /* static */ NS_METHOD
1585 sbMediaExportService::RegisterSelf(nsIComponentManager *aCompMgr,
1586  nsIFile *aPath,
1587  const char *aLoaderStr,
1588  const char *aType,
1589  const nsModuleComponentInfo *aInfo)
1590 {
1591  NS_ENSURE_ARG_POINTER(aCompMgr);
1592  NS_ENSURE_ARG_POINTER(aPath);
1593  NS_ENSURE_ARG_POINTER(aLoaderStr);
1594  NS_ENSURE_ARG_POINTER(aType);
1595  NS_ENSURE_ARG_POINTER(aInfo);
1596 
1597  nsresult rv = NS_ERROR_UNEXPECTED;
1598  nsCOMPtr<nsICategoryManager> catMgr =
1599  do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
1600  NS_ENSURE_SUCCESS(rv, rv);
1601 
1602  rv = catMgr->AddCategoryEntry("app-startup",
1604  "service,"
1606  PR_TRUE, PR_TRUE, nsnull);
1607  NS_ENSURE_SUCCESS(rv, rv);
1608 
1609  return NS_OK;
1610 }
1611 
NS_IMPL_THREADSAFE_ISUPPORTS7(sbMediaExportService, sbIMediaExportService, nsIClassInfo, nsIObserver, sbIMediaListListener, sbILocalDatabaseSmartMediaListListener, sbIJobProgress, sbIShutdownJob) NS_IMPL_CI_INTERFACE_GETTER5(sbMediaExportService
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
_updateCookies aPath
#define SB_PROPERTY_MEDIALISTNAME
const SB_LIBRARY_MANAGER_READY_TOPIC
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
#define SB_LIBRARY_MANAGER_SHUTDOWN_TOPIC
#define LOG(args)
sbStringList::const_iterator sbStringListIter
static NS_METHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *aLoaderStr, const char *aType, const nsModuleComponentInfo *aInfo)
nsresult WriteMediaItemsArray(nsIArray *aItemsArray)
#define SB_PROPERTY_ISSUBSCRIPTION
Generic interface for exposing long running jobs to the UI.
sbDeviceFirmwareAutoCheckForUpdate prototype flags
#define SB_PROPERTY_HIDDEN
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
var TASKFILE_NAME
A brief description of the contents of this interface.
sbMediaListItemMap::value_type sbMediaListItemMapPair
#define SB_MEDIAEXPORTSERVICE_CLASSNAME
#define SB_MEDIAEXPORTSERVICE_CONTRACTID
Interface used to listen to changes to a media list.
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
#define SB_THREADPOOLSERVICE_CONTRACTID
const PRUint32 LISTENER_NOTIFY_ITEM_DELTA
nsresult ListenToMediaList(sbIMediaList *aMediaList)
NS_IMETHOD OnBoolPrefChanged(const nsAString &aPrefName, const PRBool aNewPrefValue)
Songbird Thread Pool Service.
function TRACE(s)
const unsigned long LISTENER_FLAGS_AFTERITEMREMOVED
#define SB_PROPERTY_GUID
const unsigned long LISTENER_FLAGS_LISTCLEARED
nsresult SBGetLocalizedString(nsAString &aString, const nsAString &aKey, const nsAString &aDefault, class nsIStringBundle *aStringBundle)
nsresult GetShouldWatchMediaList(sbIMediaList *aMediaList, PRBool *aShouldWatch)
NS_DECL_ISUPPORTS static NS_DECL_SBIMEDIALISTENUMERATIONLISTENER sbMediaListEnumArrayHelper * New(nsIArray *aArray=nsnull)
Creates a enum array helper and initializes it.
StringArrayEnumerator prototype hasMore
const unsigned long LISTENER_FLAGS_ITEMADDED
static nsresult EnumerateItemsByGuids(typename T::const_iterator const aGuidStringListBegin, typename T::const_iterator const aGuidStringListEnd, sbIMediaList *aMediaList, nsIArray **aRetVal)
sbMediaListItemMap::iterator sbMediaListItemMapIter
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
#define SB_MEDIAEXPORTAGENTSERVICE_CONTRACTID
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER5(sbLocalDatabaseMediaItem, nsIClassInfo, nsISupportsWeakReference, nsIRequestObserver, sbILibraryResource, sbIMediaItem) sbLocalDatabaseMediaItem
Implemented to receive notifications from sbIJobProgress interfaces.
nsresult GetMainLibrary(sbILibrary **aMainLibrary)
Interface that defines a single item of media in the system.
const unsigned long LISTENER_FLAGS_ITEMUPDATED
#define SB_PROPERTY_ISLIST
#define SB_PROPERTY_CUSTOMTYPE
#define SB_PROPERTY_CONTENTURL
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
#define SB_PROPERTY_ITUNES_GUID
_getSelectedPageStyle s i
#define SB_UNUSED_IN_RELEASE(decl)
Definition: sbDebugUtils.h:55
std::list< nsString > sbStringList
nsresult GetMediaListByGuid(const nsAString &aItemGuid, sbIMediaList **aMediaList)
const unsigned short ENUMERATIONTYPE_LOCKING
This flag means that the list is protected from changes by other threads during the enumeration...
_updateTextAndScrollDataForFrame aData
function next()