sbAlbumArtScanner.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4  *
5  *=BEGIN SONGBIRD LICENSE
6  *
7  * Copyright(c) 2005-2010 POTI, Inc.
8  * http://www.songbirdnest.com
9  *
10  * For information about the licensing and copyright of this Add-On please
11  * contact POTI, Inc. at customer@songbirdnest.com.
12  *
13  *=END SONGBIRD LICENSE
14  *
15  */
16 
17 //------------------------------------------------------------------------------
18 //------------------------------------------------------------------------------
19 //
20 // Songbird album art scanner.
21 //
22 //------------------------------------------------------------------------------
23 //------------------------------------------------------------------------------
24 
30 //------------------------------------------------------------------------------
31 //
32 // Songbird album art scanner imported services.
33 //
34 //------------------------------------------------------------------------------
35 
36 // Self imports.
37 #include "sbAlbumArtScanner.h"
38 #include "sbAlbumArtCommon.h"
39 
40 // Mozilla imports.
41 #include <nsArrayUtils.h>
42 #include <nsComponentManagerUtils.h>
43 #include <nsIClassInfoImpl.h>
44 #include <nsIURI.h>
45 #include <nsIProgrammingLanguage.h>
46 #include <nsMemory.h>
47 #include <nsServiceManagerUtils.h>
48 #include <nsStringGlue.h>
49 #include <nsThreadUtils.h>
50 
51 // Songbird imports
52 #include <sbICascadeFilterSet.h>
53 #include <sbIFilterableMediaListView.h>
54 #include <sbILibraryManager.h>
55 #include <sbILibraryConstraints.h>
56 #include <sbIMediaList.h>
57 #include <sbIMediaListView.h>
58 #include <sbIPropertyArray.h>
59 #include <sbISortableMediaListView.h>
60 #include <sbPrefBranch.h>
61 #include <sbPropertiesCID.h>
62 #include <sbStandardProperties.h>
63 #include <sbStringUtils.h>
65 
72 #include "prlog.h"
73 #ifdef PR_LOGGING
74 static PRLogModuleInfo* gAlbumArtScannerLog = nsnull;
75 #define TRACE(args) PR_LOG(gAlbumArtScannerLog, PR_LOG_DEBUG, args)
76 #define LOG(args) PR_LOG(gAlbumArtScannerLog, PR_LOG_WARN, args)
77 #else
78 #define TRACE(args) /* nothing */
79 #define LOG(args) /* nothing */
80 #endif /* PR_LOGGING */
81 
82 #ifndef kNotFound
83 #define kNotFound -1
84 #endif
85 
86 //------------------------------------------------------------------------------
87 //
88 // nsISupports implementation.
89 //
90 //------------------------------------------------------------------------------
101 NS_IMPL_CI_INTERFACE_GETTER6(sbAlbumArtScanner,
102  sbIAlbumArtScanner,
103  sbIJobProgress,
104  sbIJobProgressUI,
105  sbIJobCancelable,
106  nsITimerCallback,
108 
109 NS_DECL_CLASSINFO(sbAlbumArtScanner)
110 NS_IMPL_THREADSAFE_CI(sbAlbumArtScanner)
111 
112 
113 //------------------------------------------------------------------------------
114 //
115 // sbIAlbumArtScanner implementation.
116 //
117 //------------------------------------------------------------------------------
118 
124 NS_IMETHODIMP
125 sbAlbumArtScanner::ScanListForArtwork(sbIMediaList* aMediaList)
126 {
127  TRACE(("sbAlbumArtScanner[0x%8.x] - ScanListForArtwork", this));
128  nsresult rv = NS_OK;
129 
130  nsCOMPtr<sbIMediaList> mediaList = aMediaList;
131 
132  // If aMediaList is null then we need to grab the main library
133  // TODO: Should we make a copy?
134  if (aMediaList == nsnull) {
135  nsCOMPtr<sbILibraryManager> libManager =
136  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
137  NS_ENSURE_SUCCESS(rv, rv);
138 
139  nsCOMPtr<sbILibrary> mLibrary;
140  rv = libManager->GetMainLibrary(getter_AddRefs(mLibrary));
141  NS_ENSURE_SUCCESS(rv, rv);
142 
143  mediaList = do_QueryInterface(mLibrary, &rv);
144  NS_ENSURE_SUCCESS(rv, rv);
145  }
146 
147  // Now create a view and get the filter set so that we can filter/sort the
148  // items
149  rv = mediaList->CreateView(nsnull, getter_AddRefs(mMediaListView));
150  NS_ENSURE_SUCCESS(rv, rv);
151 
152  // Filter out the lists (it would be nice to filter out the items that
153  // already have primaryImageURL set)
154  nsCOMPtr<sbIFilterableMediaListView> filterView =
155  do_QueryInterface(mMediaListView, &rv);
156  NS_ENSURE_SUCCESS(rv, rv);
157 
158  // get the old constraints
159  nsCOMPtr<sbILibraryConstraint> constraint;
160  rv = filterView->GetFilterConstraint(getter_AddRefs(constraint));
161  NS_ENSURE_SUCCESS(rv, rv);
162 
163  nsCOMPtr<sbILibraryConstraintBuilder> builder =
164  do_CreateInstance("@songbirdnest.com/Songbird/Library/ConstraintBuilder;1",
165  &rv);
166  NS_ENSURE_SUCCESS(rv, rv);
167 
168  // push the original constraints into it if there's an existing one
169  if (constraint) {
170  rv = builder->IncludeConstraint(constraint, nsnull);
171  NS_ENSURE_SUCCESS(rv, rv);
172  rv = builder->Intersect(nsnull);
173  NS_ENSURE_SUCCESS(rv, rv);
174  }
175 
176  // Add the isList and Hidden filters
177  rv = builder->Include(NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
178  NS_LITERAL_STRING("0"),
179  nsnull);
180  NS_ENSURE_SUCCESS(rv, rv);
181  rv = builder->Intersect(nsnull);
182  NS_ENSURE_SUCCESS(rv, rv);
183  rv = builder->Include(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
184  NS_LITERAL_STRING("0"),
185  nsnull);
186 
187  // Only look up audio tracks, and not video / podcasts.
188  rv = builder->Intersect(nsnull);
189  NS_ENSURE_SUCCESS(rv, rv);
190  rv = builder->Include(NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE),
191  NS_LITERAL_STRING("audio"),
192  nsnull);
193  NS_ENSURE_SUCCESS(rv, rv);
194 
195  // Now reset the constraint on the view
196  rv = builder->Get(getter_AddRefs(constraint));
197  NS_ENSURE_SUCCESS(rv, rv);
198  rv = filterView->SetFilterConstraint(constraint);
199  NS_ENSURE_SUCCESS(rv, rv);
200 
201  // Now sort the items
202  // Sort by:
203  // AlbumName - Asc
204  // AlbumArtistName - Asc
205  // ArtistName - Asc
206  // Disc Number - Asc
207  // Track Number - Asc
208  nsCOMPtr<sbIMutablePropertyArray> newSort =
209  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
210  NS_ENSURE_SUCCESS(rv, rv);
211 
212  rv = newSort->SetStrict(PR_FALSE);
213  NS_ENSURE_SUCCESS(rv, rv);
214 
215  // AlbumName
216  rv = newSort->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ALBUMNAME),
217  NS_LITERAL_STRING("a"));
218  NS_ENSURE_SUCCESS(rv, rv);
219 
220  // AlbumArtistName
221  rv = newSort->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ALBUMARTISTNAME),
222  NS_LITERAL_STRING("a"));
223  NS_ENSURE_SUCCESS(rv, rv);
224 
225  // ArtistName
226  rv = newSort->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ARTISTNAME),
227  NS_LITERAL_STRING("a"));
228  NS_ENSURE_SUCCESS(rv, rv);
229 
230  // Disc Number
231  rv = newSort->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_DISCNUMBER),
232  NS_LITERAL_STRING("a"));
233  NS_ENSURE_SUCCESS(rv, rv);
234 
235  // Track Number
236  rv = newSort->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_TRACKNUMBER),
237  NS_LITERAL_STRING("a"));
238  NS_ENSURE_SUCCESS(rv, rv);
239 
240  // Now set the sort on this list
241  nsCOMPtr<sbISortableMediaListView> sortable =
242  do_QueryInterface(mMediaListView, &rv);
243  NS_ENSURE_SUCCESS(rv, rv);
244 
245  rv = sortable->SetSort(newSort);
246  NS_ENSURE_SUCCESS(rv, rv);
247 
248  // Reset all the progress information
249  rv = mMediaListView->GetLength(&mTotalItemCount);
250  NS_ENSURE_SUCCESS(rv, rv);
251 
252  mCompletedItemCount = 0;
253  mProcessNextAlbum = PR_TRUE;
254 
255  // Update the progress and inform listeners
256  UpdateProgress();
257 
258  // Start up the interval timer that will process the albums/items
259  rv = mIntervalTimer->InitWithCallback(this,
260  mIntervalTimerValue,
261  nsITimer::TYPE_REPEATING_SLACK);
262  NS_ENSURE_SUCCESS(rv, rv);
263 
264  return rv;
265 }
266 
267 //
268 // Getters/setters.
269 //
270 
275 NS_IMETHODIMP
276 sbAlbumArtScanner::GetUpdateArtwork(PRBool* _retval)
277 {
278  TRACE(("%s[%.8x] = %d", __FUNCTION__, this, mUpdateArtwork));
279  NS_ENSURE_ARG_POINTER(_retval);
280  *_retval = mUpdateArtwork;
281  return NS_OK;
282 }
283 
284 NS_IMETHODIMP
285 sbAlbumArtScanner::SetUpdateArtwork(PRBool aUpdateArtwork)
286 {
287  TRACE(("%s[%.8x] = %d", __FUNCTION__, this, aUpdateArtwork));
288  mUpdateArtwork = aUpdateArtwork;
289  return NS_OK;
290 }
291 
292 //------------------------------------------------------------------------------
293 //
294 // sbIJobProgress implementation.
295 //
296 //------------------------------------------------------------------------------
297 
298 /* readonly attribute unsigned short status; */
299 NS_IMETHODIMP sbAlbumArtScanner::GetStatus(PRUint16* aStatus)
300 {
301  TRACE(("sbAlbumArtScanner[0x%8.x] - GetStatus", this));
302  NS_ENSURE_ARG_POINTER( aStatus );
303  *aStatus = mStatus;
304  return NS_OK;
305 }
306 
307 /* readonly attribute boolean blocked; */
308 NS_IMETHODIMP sbAlbumArtScanner::GetBlocked(PRBool* aBlocked)
309 {
310  TRACE(("sbAlbumArtScanner[0x%8.x] - GetBlocked", this));
311  NS_ENSURE_ARG_POINTER( aBlocked );
312  *aBlocked = PR_FALSE;
313  return NS_OK;
314 }
315 
316 /* readonly attribute unsigned AString statusText; */
317 NS_IMETHODIMP sbAlbumArtScanner::GetStatusText(nsAString& aText)
318 {
319  TRACE(("sbAlbumArtScanner[0x%8.x] - GetStatusText", this));
320  NS_ASSERTION(NS_IsMainThread(), \
321  "sbAlbumArtScanner::GetStatusText is main thread only!");
322  nsresult rv;
323 
324  if (mStatus == sbIJobProgress::STATUS_RUNNING) {
325  nsresult rv = NS_OK;
326  nsString outMessage;
327  nsString stringKey;
328 
329  const PRUnichar *strings[2] = {
330  mCurrentAlbumName.get(),
331  mCurrentFetcherName.get()
332  };
333  if (mCurrentFetcherName.IsEmpty()) {
334  stringKey.AssignLiteral("albumart.scanning.nofetcher.message");
335  } else {
336  stringKey.AssignLiteral("albumart.scanning.fetcher.message");
337  }
338  rv = mStringBundle->FormatStringFromName(stringKey.get(),
339  strings,
340  NS_ARRAY_LENGTH(strings),
341  getter_Copies(outMessage));
342 
343  if (NS_FAILED(rv)) {
344  aText.Assign(stringKey);
345  } else {
346  aText.Assign(outMessage);
347  }
348  } else {
349  rv = mStringBundle->GetStringFromName(
350  NS_LITERAL_STRING("albumart.scanning.completed").get(),
351  getter_Copies(mTitleText));
352  if (NS_FAILED(rv)) {
353  aText.AssignLiteral("albumart.scanning.completed");
354  }
355  }
356  return NS_OK;
357 }
358 
359 /* readonly attribute AString titleText; */
360 NS_IMETHODIMP sbAlbumArtScanner::GetTitleText(nsAString& aText)
361 {
362  TRACE(("sbAlbumArtScanner[0x%8.x] - GetTitleText", this));
363  nsresult rv;
364 
365  // If we haven't figured out a title yet, get one from the
366  // string bundle.
367  if (mTitleText.IsEmpty()) {
368  rv = mStringBundle->GetStringFromName(
369  NS_LITERAL_STRING("albumart.scanning.title").get(),
370  getter_Copies(mTitleText));
371  if (NS_FAILED(rv)) {
372  mTitleText.AssignLiteral("albumart.scanning.title");
373  }
374  }
375 
376  aText = mTitleText;
377  return NS_OK;
378 }
379 
380 
381 /* readonly attribute unsigned long progress; */
382 NS_IMETHODIMP sbAlbumArtScanner::GetProgress(PRUint32* aProgress)
383 {
384  TRACE(("sbAlbumArtScanner[0x%8.x] - GetProgress", this));
385  NS_ENSURE_ARG_POINTER( aProgress );
386  NS_ASSERTION(NS_IsMainThread(), \
387  "sbAlbumArtScanner::GetProgress is main thread only!");
388 
389  *aProgress = mCompletedItemCount;
390  return NS_OK;
391 }
392 
393 /* readonly attribute unsigned long total; */
394 NS_IMETHODIMP sbAlbumArtScanner::GetTotal(PRUint32* aTotal)
395 {
396  TRACE(("sbAlbumArtScanner[0x%8.x] - GetTotal", this));
397  NS_ENSURE_ARG_POINTER( aTotal );
398  NS_ASSERTION(NS_IsMainThread(), \
399  "sbAlbumArtScanner::GetTotal is main thread only!");
400 
401  if (mTotalItemCount > 1) {
402  *aTotal = mTotalItemCount;
403  } else {
404  *aTotal = 0; // indeterminate
405  }
406  return NS_OK;
407 }
408 
409 /* readonly attribute unsigned long errorCount; */
410 NS_IMETHODIMP sbAlbumArtScanner::GetErrorCount(PRUint32* aCount)
411 {
412  TRACE(("sbAlbumArtScanner[0x%8.x] - GetErrorCount", this));
413  NS_ENSURE_ARG_POINTER( aCount );
414  NS_ASSERTION(NS_IsMainThread(), \
415  "sbAlbumArtScanner::GetErrorCount is main thread only!");
416 
417  *aCount = mErrorMessages.Length();
418  return NS_OK;
419 }
420 
421 /* nsIStringEnumerator getErrorMessages(); */
422 NS_IMETHODIMP sbAlbumArtScanner::GetErrorMessages(nsIStringEnumerator** aMessages)
423 {
424  TRACE(("sbAlbumArtScanner[0x%8.x] - GetErrorMessages", this));
425  NS_ENSURE_ARG_POINTER(aMessages);
426  NS_ASSERTION(NS_IsMainThread(), \
427  "sbAlbumArtScanner::GetProgress is main thread only!");
428 
429  *aMessages = nsnull;
430 
431  nsCOMPtr<nsIStringEnumerator> enumerator =
432  new sbTArrayStringEnumerator(&mErrorMessages);
433  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
434 
435  enumerator.forget(aMessages);
436  return NS_OK;
437 }
438 
439 /* void addJobProgressListener( in sbIJobProgressListener aListener ); */
440 NS_IMETHODIMP
441 sbAlbumArtScanner::AddJobProgressListener(sbIJobProgressListener *aListener)
442 {
443  TRACE(("sbAlbumArtScanner[0x%8.x] - AddJobProgressListener", this));
444  NS_ENSURE_ARG_POINTER(aListener);
445  NS_ASSERTION(NS_IsMainThread(), \
446  "sbAlbumArtScanner::AddJobProgressListener is main thread only!");
447 
448  PRInt32 index = mListeners.IndexOf(aListener);
449  if (index >= 0) {
450  // the listener already exists, do not re-add
451  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
452  }
453  PRBool succeeded = mListeners.AppendObject(aListener);
454  return succeeded ? NS_OK : NS_ERROR_FAILURE;
455 }
456 
457 /* void removeJobProgressListener( in sbIJobProgressListener aListener ); */
458 NS_IMETHODIMP
459 sbAlbumArtScanner::RemoveJobProgressListener(sbIJobProgressListener* aListener)
460 {
461  TRACE(("sbAlbumArtScanner[0x%8.x] - RemoveJobProgressListener", this));
462  NS_ENSURE_ARG_POINTER(aListener);
463  NS_ASSERTION(NS_IsMainThread(), \
464  "sbAlbumArtScanner::RemoveJobProgressListener is main thread only!");
465 
466  PRInt32 indexToRemove = mListeners.IndexOf(aListener);
467  if (indexToRemove < 0) {
468  // Err, no such listener
469  return NS_ERROR_UNEXPECTED;
470  }
471 
472  // remove the listener
473  PRBool succeeded = mListeners.RemoveObjectAt(indexToRemove);
474  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
475 
476  return NS_OK;
477 }
478 
479 //------------------------------------------------------------------------------
480 //
481 // sbIJobProgressUI Implementation.
482 //
483 //------------------------------------------------------------------------------
484 
485 /* attribute DOMString crop; */
486 NS_IMETHODIMP sbAlbumArtScanner::GetCrop(nsAString & aCrop)
487 {
488  aCrop.AssignLiteral("end");
489  return NS_OK;
490 }
491 
492 //------------------------------------------------------------------------------
493 //
494 // sbIJobCancelable Implementation.
495 //
496 //------------------------------------------------------------------------------
497 
498 /* boolean canCancel; */
499 NS_IMETHODIMP sbAlbumArtScanner::GetCanCancel(PRBool* _retval)
500 {
501  TRACE(("sbAlbumArtScanner[0x%8.x] - GetCanCancel", this));
502  *_retval = PR_TRUE;
503  return NS_OK;
504 }
505 
506 /* void cancel(); */
507 NS_IMETHODIMP sbAlbumArtScanner::Cancel()
508 {
509  TRACE(("sbAlbumArtScanner[0x%8.x] - Cancel", this));
510  NS_ASSERTION(NS_IsMainThread(), \
511  "sbAlbumArtScanner::Cancel is main thread only!");
512 
513  // Indicate that we have stopped and call UpdateProgress to take care of cleanup.
515  UpdateProgress();
516 
517  return NS_OK;
518 }
519 
520 //------------------------------------------------------------------------------
521 //
522 // nsITimerCallback Implementation.
523 //
524 //------------------------------------------------------------------------------
525 
526 /* notify(in nsITimer aTimer); */
527 NS_IMETHODIMP sbAlbumArtScanner::Notify(nsITimer* aTimer)
528 {
529  NS_ENSURE_ARG_POINTER(aTimer);
530  nsresult rv;
531 
532  if (aTimer == mIntervalTimer) {
533  if (mProcessNextAlbum) {
534  rv = ProcessAlbum();
535  if (NS_FAILED(rv)) {
536  TRACE(("sbAlbumArtScanner::Notify - Fetch Failed for album"));
537  // Hmm, not good so we should skip this album
538  mProcessNextAlbum = PR_TRUE;
539  }
540  }
541  }
542  return NS_OK;
543 }
544 
545 
546 //------------------------------------------------------------------------------
547 //
548 // sbIAlbumArtListener Implementation.
549 //
550 //------------------------------------------------------------------------------
551 
552 /* onChangeFetcher(in sbIAlbumArtFetcher aFetcher); */
553 NS_IMETHODIMP
554 sbAlbumArtScanner::OnChangeFetcher(sbIAlbumArtFetcher* aFetcher)
555 {
556  TRACE(("sbAlbumArtScanner[0x%8.x] - OnChangeFetcher", this));
557  mCurrentFetcher = aFetcher;
558  aFetcher->GetName(mCurrentFetcherName);
559  UpdateProgress();
560  return NS_OK;
561 }
562 
563 /* onTrackResult(in nsIURI aImageLocation, in sbIMediaItem aMediaItem); */
564 NS_IMETHODIMP
565 sbAlbumArtScanner::OnTrackResult(nsIURI* aImageLocation,
566  sbIMediaItem* aMediaItem)
567 {
568  TRACE(("sbAlbumArtScanner[0x%8.x] - OnResult", this));
569  NS_ENSURE_ARG_POINTER(aMediaItem);
570  nsresult rv;
571 
572  // If fetcher is remote, mark that an attempt was made to fetch remote art for
573  // the item.
574  if (mCurrentFetcher) {
575  PRBool isLocal;
576  rv = mCurrentFetcher->GetIsLocal(&isLocal);
577  NS_ENSURE_SUCCESS(rv, rv);
578  if (!isLocal) {
579  rv = MarkRemoteFetchAttempted(aMediaItem);
580  NS_ENSURE_SUCCESS(rv, rv);
581  }
582  }
583 
584  // A null aImageLocation indicates a failure
585  if (aImageLocation) {
586  rv = SetItemArtwork(aImageLocation, aMediaItem);
587  NS_ENSURE_SUCCESS(rv, rv);
588  }
589 
590  return NS_OK;
591 }
592 
593 /* onAlbumResult(in nsIURI aImageLocation, in nsIArray aMediaItems); */
594 NS_IMETHODIMP
595 sbAlbumArtScanner::OnAlbumResult(nsIURI* aImageLocation,
596  nsIArray* aMediaItems)
597 {
598  TRACE(("sbAlbumArtScanner[0x%8.x] - OnAlbumResult", this));
599  NS_ENSURE_ARG_POINTER(aMediaItems);
600  nsresult rv;
601 
602  // If fetcher is remote, mark that an attempt was made to fetch remote art for
603  // the items.
604  if (mCurrentFetcher) {
605  PRBool isLocal;
606  rv = mCurrentFetcher->GetIsLocal(&isLocal);
607  NS_ENSURE_SUCCESS(rv, rv);
608  if (!isLocal) {
609  PRUint32 itemCount;
610  rv = aMediaItems->GetLength(&itemCount);
611  NS_ENSURE_SUCCESS(rv, rv);
612  for (PRUint32 i = 0; i < itemCount; i++) {
613  nsCOMPtr<sbIMediaItem>
614  mediaItem = do_QueryElementAt(aMediaItems, i, &rv);
615  NS_ENSURE_SUCCESS(rv, rv);
616  rv = MarkRemoteFetchAttempted(mediaItem);
617  NS_ENSURE_SUCCESS(rv, rv);
618  }
619  }
620  }
621 
622  // A null aImageLocation indicates a failure
623  if (aImageLocation) {
624  rv = SetItemsArtwork(aImageLocation, aMediaItems);
625  NS_ENSURE_SUCCESS(rv, rv);
626  }
627 
628  return NS_OK;
629 }
630 
631 /* onSearchComplete(in nsIArray aMediaItems); */
632 NS_IMETHODIMP
633 sbAlbumArtScanner::OnSearchComplete(nsIArray* aMediaItems)
634 {
635  TRACE(("sbAlbumArtScanner[0x%8.x] - OnSearchComplete", this));
636  nsresult rv;
637 
638  // Done with the fetchers for now
639  mCurrentFetcher = nsnull;
640 
641  // Now that we are done this album move on to the next
642  mProcessNextAlbum = PR_TRUE;
643 
644  // Write the images to metadata
645  if (aMediaItems) {
646  rv = WriteImageMetadata(aMediaItems);
647  NS_ENSURE_SUCCESS(rv, rv);
648  }
649 
650  return NS_OK;
651 }
652 
653 //------------------------------------------------------------------------------
654 //
655 // Public services.
656 //
657 //------------------------------------------------------------------------------
658 
664  mIntervalTimerValue(ALBUMART_SCANNER_INTERVAL),
665  mUpdateArtwork(PR_FALSE),
666  mStatus(sbIJobProgress::STATUS_RUNNING),
667  mCompletedItemCount(0),
668  mTotalItemCount(0),
669  mProcessNextAlbum(PR_FALSE),
670  mCurrentAlbumItemList(nsnull),
671  mMediaListView(nsnull)
672 {
673 #ifdef PR_LOGGING
674  if (!gAlbumArtScannerLog) {
675  gAlbumArtScannerLog = PR_NewLogModule("sbAlbumArtScanner");
676  }
677 #endif
678  TRACE(("sbAlbumArtScanner[0x%.8x] - ctor", this));
679  MOZ_COUNT_CTOR(sbAlbumArtScanner);
680 }
681 
687 {
688  TRACE(("sbAlbumArtScanner[0x%.8x] - dtor", this));
689  MOZ_COUNT_DTOR(sbAlbumArtScanner);
690  if (mIntervalTimer) {
691  mIntervalTimer->Cancel();
692  mIntervalTimer = nsnull;
693  }
694  mFetcherSet = nsnull;
695  mCurrentFetcher = nsnull;
696  mCurrentAlbumItemList = nsnull;
697  mStringBundle = nsnull;
698 }
699 
704 nsresult
706 {
707  TRACE(("sbAlbumArtScanner[0x%.8x] - Initialize", this));
708  nsresult rv = NS_OK;
709 
710  // Create our timer
711  mIntervalTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
712  NS_ENSURE_SUCCESS(rv, rv);
713 
714  // Get our timer values
715  // Get the preference branch.
717  NS_ENSURE_SUCCESS(rv, rv);
718 
719  mIntervalTimerValue = prefBranch.GetIntPref(PREF_ALBUMART_SCANNER_INTERVAL,
721 
722  // Create our fetcher set
723  mFetcherSet =
724  do_CreateInstance("@songbirdnest.com/Songbird/album-art-fetcher-set;1", &rv);
725  NS_ENSURE_SUCCESS(rv, rv);
726  rv = mFetcherSet->SetFetcherType(sbIAlbumArtFetcherSet::TYPE_ALL);
727  NS_ENSURE_SUCCESS(rv, rv);
728 
729  // Create an array for items in an album
730  mCurrentAlbumItemList =
731  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
732  NS_ENSURE_SUCCESS(rv, rv);
733 
734  // Grab our string bundle
735  nsCOMPtr<nsIStringBundleService> StringBundleService =
736  do_GetService("@mozilla.org/intl/stringbundle;1", &rv );
737  NS_ENSURE_SUCCESS(rv, rv);
738 
739  rv = StringBundleService->CreateBundle(
740  "chrome://songbird/locale/songbird.properties",
741  getter_AddRefs(mStringBundle));
742  NS_ENSURE_SUCCESS(rv, rv);
743 
744 
745  return rv;
746 }
747 
748 //------------------------------------------------------------------------------
749 //
750 // Private services.
751 //
752 //------------------------------------------------------------------------------
753 
754 nsresult
755 sbAlbumArtScanner::UpdateProgress()
756 {
757  TRACE(("sbAlbumArtScanner[0x%.8x] - UpdateProgress", this));
758  NS_ASSERTION(NS_IsMainThread(), \
759  "sbAlbumArtScanner::UpdateProgress is main thread only!");
760 
761  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
762  // We are done so shut down, we do this before notifying the
763  // listeners since they may take some time and we need to cancel
764  // the timers as soon as possible.
765  TRACE(("sbAlbumArtScanner::UpdateProgress - Shutting down Job"));
766  mCurrentFetcher = nsnull;
767  mProcessNextAlbum = PR_FALSE;
768  mIntervalTimer->Cancel();
769  mFetcherSet->Shutdown();
770  }
771 
772  for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i) {
773  mListeners[i]->OnJobProgress(this);
774  }
775 
776  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
777  // We're done. No more notifications needed.
778  mListeners.Clear();
779  }
780 
781  return NS_OK;
782 }
783 
784 nsresult
785 sbAlbumArtScanner::GetNextAlbumItems()
786 {
787  TRACE(("sbAlbumArtScanner[0x%.8x] - GetNextAlbumItems [%d/%d]",
788  this,
789  mCompletedItemCount,
790  mTotalItemCount));
791  nsresult rv;
792 
793  nsString mLastAlbumName;
794  nsString mLastArtistName;
795 
796  // Clear the item list so we can start fresh
797  mCurrentAlbumItemList->Clear();
798 
799  // Loop while we still have items and we haven't gotten to the next album
800  // We need to check the albumName first with the previous one, then if that
801  // matches we can check the albumArtist/artist with previous artist.
802  // If all that matches then we are still on the same album and we do a check
803  // to see if the image has already been found or not (since we can not filter
804  // out non-null properties).
805  while (mCompletedItemCount < mTotalItemCount) {
806  TRACE(("sbAlbumArtScanner - Processing %d of %d",
807  mCompletedItemCount,
808  mTotalItemCount));
809 
810  nsCOMPtr<sbIMediaItem> item;
811  rv = mMediaListView->GetItemByIndex(mCompletedItemCount,
812  getter_AddRefs(item));
813  if (NS_FAILED(rv)) {
814  // Move on to the next item
815  TRACE(("sbAlbumArtScanner - Processing : Item %d failed with %08X",
816  mCompletedItemCount,
817  rv));
818  mCompletedItemCount++;
819  continue;
820  }
821  TRACE(("sbAlbumArtScanner - Found Item %d", mCompletedItemCount));
822 
823  // We need an album name or this is completely pointless.
824  nsString albumName;
825  rv = item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ALBUMNAME), albumName);
826  if (NS_FAILED(rv) || albumName.IsEmpty()) {
827  // Move on to the next item
828  mCompletedItemCount++;
829  continue;
830  }
831 
832  // Next we use either the albumArtistName (preferred) or artistName
833  nsString albumArtistName;
834  item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ALBUMARTISTNAME),
835  albumArtistName);
836  // If the albumArtistName fails we will fall back to the artist name
837 
838  nsString artistName;
839  if (!albumArtistName.IsEmpty()) {
840  // Try to use the album artist name if possible
841  artistName = albumArtistName;
842  } else {
843  rv = item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ARTISTNAME),
844  artistName);
845  if (NS_FAILED(rv)) {
846  mCompletedItemCount++;
847  continue;
848  }
849  }
850 
851  if (artistName.IsEmpty()) {
852  // No artist then how are we going to know who this album belongs to?
853  mCompletedItemCount++;
854  continue;
855  }
856 
857 #ifdef PR_LOGGING
858  // Adding track name for debugging
859  nsString trackName;
860  rv = item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_TRACKNAME), trackName);
861  NS_ENSURE_SUCCESS(rv, rv);
862  TRACE(("sbAlbumArtScanner - Processing Track [%s] by [%s] from [%s]",
863  NS_ConvertUTF16toUTF8(trackName).get(),
864  NS_ConvertUTF16toUTF8(artistName).get(),
865  NS_ConvertUTF16toUTF8(albumName).get()
866  ));
867 #endif
868 
869  // if this is the first album then just set the Last* values and append the
870  // item to the list
871  if (mLastAlbumName.IsEmpty()) {
872  mLastAlbumName.Assign(albumName);
873  mCurrentAlbumName.Assign(albumName);
874  mLastArtistName.Assign(artistName);
875  TRACE(("sbAlbumArtScanner - First instance of album."));
876  } else if (!mLastAlbumName.Equals(albumName)) {
877  // if the album names are different then we definitly have a new album
878  // so don't add this track or increment the index, just break out of the
879  // loop.
880  TRACE(("sbAlbumArtScanner - Sending album to be processed for album art."));
881  break;
882  } else {
883  // Check if the artist is the same as the previous artist
884  TRACE(("sbAlbumArtScanner - Checking artist: prev %s, current %s",
885  NS_ConvertUTF16toUTF8(mLastArtistName).get(),
886  NS_ConvertUTF16toUTF8(artistName).get()
887  ));
888  if (!mLastArtistName.Equals(artistName) &&
889  (artistName.Find(mLastArtistName, PR_TRUE) == kNotFound) &&
890  (mLastArtistName.Find(artistName, PR_TRUE) == kNotFound)) {
891  // No substring so this must not be the same artist
892  TRACE(("sbAlbumArtScanner - Sending album to be processed for album art."));
893  break;
894  }
895  }
896 
897  // If not updating artwork, skip items that already have album art.
898  if (!mUpdateArtwork) {
899  nsString primaryImageUrl;
900  rv = item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL),
901  primaryImageUrl);
902  if (NS_FAILED(rv) || !primaryImageUrl.IsEmpty()) {
903  TRACE(("sbAlbumArtScanner - Item already has cover."));
904  // Move on to the next item
905  mCompletedItemCount++;
906  continue;
907  }
908  }
909 
910  TRACE(("sbAlbumArtScanner - Adding to list"));
911  rv = mCurrentAlbumItemList->AppendElement(NS_ISUPPORTS_CAST(sbIMediaItem *,
912  item),
913  PR_FALSE);
914  NS_ENSURE_SUCCESS(rv, rv);
915  mCompletedItemCount++;
916  }
917 
918  return NS_OK;
919 }
920 
921 nsresult
922 sbAlbumArtScanner::ProcessAlbum()
923 {
924  TRACE(("sbAlbumArtScanner[0x%.8x] - ProcessAlbum", this));
925  nsresult rv = NS_OK;
926 
927  // Clear our flags
928  mProcessNextAlbum = PR_FALSE;
929 
930  rv = GetNextAlbumItems();
931  NS_ENSURE_SUCCESS(rv, rv);
932 
933  PRUint32 trackCount = 0;
934  rv = mCurrentAlbumItemList->GetLength(&trackCount);
935  NS_ENSURE_SUCCESS(rv, rv);
936  TRACE(("Collected %d of %d items, current list has %d items",
937  mCompletedItemCount,
938  mTotalItemCount,
939  trackCount));
940  if (trackCount > 0) {
941  TRACE(("sbAlbumArtScanner::ProcessAlbum - Fetching artwork for items."));
942  mCurrentFetcherName.Truncate();
943  UpdateProgress();
944  rv = mFetcherSet->FetchAlbumArtForAlbum(mCurrentAlbumItemList, this);
945  NS_ENSURE_SUCCESS(rv, rv);
946  } else if (mCompletedItemCount >= mTotalItemCount) {
947  // We need to shut everything down.
948  TRACE(("sbAlbumArtScanner::ProcessAlbum - All albums scanned."));
950  UpdateProgress();
951  } else {
952  // We have items left but no items in mCurrentAlbumItemList, this probably
953  // means we have all the artwork for this album, just move on to the next.
954  UpdateProgress();
955  mProcessNextAlbum = PR_TRUE;
956  }
957 
958  return NS_OK;
959 }
960 
961 nsresult
962 sbAlbumArtScanner::MarkRemoteFetchAttempted(sbIMediaItem* aMediaItem)
963 {
964  TRACE(("sbAlbumArtScanner[0x%8.x] - MarkRemoteFetchAttempted", this));
965  NS_ENSURE_ARG_POINTER(aMediaItem);
966  nsresult rv;
967 
968  // Set attempted remote art fetch property if not already set.
969  nsAutoString attemptedRemoteArtFetch;
970  rv = aMediaItem->GetProperty
971  (NS_LITERAL_STRING(SB_PROPERTY_ATTEMPTED_REMOTE_ART_FETCH),
972  attemptedRemoteArtFetch);
973  NS_ENSURE_SUCCESS(rv, rv);
974  if (!attemptedRemoteArtFetch.Equals(NS_LITERAL_STRING("1"))) {
975  rv = aMediaItem->SetProperty
976  (NS_LITERAL_STRING(SB_PROPERTY_ATTEMPTED_REMOTE_ART_FETCH),
977  NS_LITERAL_STRING("1"));
978  NS_ENSURE_SUCCESS(rv, rv);
979  }
980 
981  return NS_OK;
982 }
983 
return NS_OK
Interface to control UI aspects of sbIJobProgress.
NS_DECL_ISUPPORTS NS_DECL_SBIALBUMARTSCANNER NS_DECL_NSICLASSINFO NS_DECL_SBIJOBPROGRESS NS_DECL_SBIJOBPROGRESSUI NS_DECL_SBIJOBCANCELABLE NS_DECL_NSITIMERCALLBACK NS_DECL_SBIALBUMARTLISTENER sbAlbumArtScanner()
function succeeded(ch, cx, status, data)
#define SB_PROPERTY_ALBUMARTISTNAME
Generic interface for exposing long running jobs to the UI.
#define SB_PROPERTY_HIDDEN
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER6(sbDeviceLibrary, nsIClassInfo, sbIDeviceLibrary, sbILibrary, sbIMediaList, sbIMediaItem, sbILibraryResource) sbDeviceLibrary
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
Interface for an album art scanner. Instantiate as a component instance. It also implements the sbIJo...
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
A brief description of the contents of this interface.
NS_IMPL_THREADSAFE_RELEASE(sbRequestItem)
NS_IMPL_THREADSAFE_ADDREF(sbRequestItem)
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
var strings
Definition: Info.js:46
A component which is interested in the result of an album art fetch request.
#define SB_PROPERTY_ATTEMPTED_REMOTE_ART_FETCH
#define SB_PROPERTY_CONTENTTYPE
Songbird Album Art Scanner Definitions.
#define kNotFound
#define SB_PROPERTY_DISCNUMBER
#define SB_PROPERTY_ARTISTNAME
nsresult SetItemsArtwork(nsIURI *aImageLocation, nsIArray *aMediaItems)
#define ALBUMART_SCANNER_INTERVAL
const unsigned long TYPE_ALL
#define SB_PROPERTY_ALBUMNAME
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
#define PREF_ALBUMART_SCANNER_BRANCH
#define TRACE(args)
NS_IMPL_QUERY_INTERFACE7_CI(sbAlbumArtScanner, sbIAlbumArtScanner, nsIClassInfo, sbIJobProgress, sbIJobProgressUI, sbIJobCancelable, nsITimerCallback, sbIAlbumArtListener) NS_IMPL_CI_INTERFACE_GETTER6(sbAlbumArtScanner
Implemented to receive notifications from sbIJobProgress interfaces.
Interface that defines a single item of media in the system.
#define PREF_ALBUMART_SCANNER_INTERVAL
#define SB_PROPERTY_TRACKNAME
restoreHistoryPrecursor aCount
nsresult WriteImageMetadata(nsIArray *aMediaItems)
Interface for an album art fetcher. Instantiate as a component instance.
#define SB_PROPERTY_ISLIST
const unsigned short STATUS_FAILED
Constant indicating that the job has completed with errors.
nsresult SetItemArtwork(nsIURI *aImageLocation, sbIMediaItem *aMediaItem)
_getSelectedPageStyle s i
#define SB_PROPERTY_PRIMARYIMAGEURL
nsITimerCallback
#define SB_PROPERTY_TRACKNUMBER
PRInt32 GetIntPref(const char *aKey, const PRInt32 aDefault)
Definition: sbPrefBranch.h:128