sbMetadataJob.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3 //
4 // BEGIN SONGBIRD GPL
5 //
6 // This file is part of the Songbird web player.
7 //
8 // Copyright(c) 2005-2008 POTI, Inc.
9 // http://songbirdnest.com
10 //
11 // This file may be licensed under the terms of of the
12 // GNU General Public License Version 2 (the "GPL").
13 //
14 // Software distributed under the License is distributed
15 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
16 // express or implied. See the GPL for the specific language
17 // governing rights and limitations.
18 //
19 // You should have received a copy of the GPL along with this
20 // program. If not, go to http://www.gnu.org/licenses/gpl.html
21 // or write to the Free Software Foundation, Inc.,
22 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 //
24 // END SONGBIRD GPL
25 //
26 */
27 
33 // INCLUDES ===================================================================
34 #include <nspr.h>
35 #include <nscore.h>
36 #include <nsAutoLock.h>
37 #include <pratom.h>
38 
39 #include <nsArrayUtils.h>
40 #include <nsUnicharUtils.h>
41 #include <nsComponentManagerUtils.h>
42 #include <nsServiceManagerUtils.h>
43 #include <nsCRTGlue.h>
44 #include <nsIFile.h>
45 #include <nsIFileURL.h>
46 #include <nsAutoPtr.h>
47 #include <nsThreadUtils.h>
48 #include <nsMemory.h>
49 #include <nsNetUtil.h>
50 #include <nsIClassInfoImpl.h>
51 #include <nsIProgrammingLanguage.h>
52 #include <nsIURI.h>
53 #include <nsIIOService.h>
54 #include <nsIPrefService.h>
55 #include <nsIPrefBranch2.h>
56 #include <nsIStringBundle.h>
57 
58 #include <sbIFileMetadataService.h>
59 #include <sbILibrary.h>
60 #include <sbILibraryManager.h>
61 #include <sbILibraryResource.h>
62 #include <sbILibraryUtils.h>
63 #include <sbILocalDatabaseLibrary.h>
64 #include <sbLocalDatabaseLibrary.h>
65 #include <sbILocalDatabaseMediaItem.h>
66 #include <sbIPropertyArray.h>
67 #include <sbIPropertyInfo.h>
68 #include <sbIPropertyManager.h>
69 #include <sbIMediaItem.h>
70 #include <sbIMediaList.h>
71 #include <sbStandardProperties.h>
72 #include <sbPropertiesCID.h>
73 #include <sbIAlbumArtFetcherSet.h>
74 #include <sbIWatchFolderService.h>
75 
76 #include <sbStringBundle.h>
77 #include <sbStringUtils.h>
79 
80 #include "sbIMetadataManager.h"
81 #include "sbIMetadataHandler.h"
82 #include "sbMetadataJob.h"
83 #include "sbMetadataJobItem.h"
84 
85 
86 // DEFINES ====================================================================
87 
88 // Queue up 50 job items in mProcessedBackgroundThreadItems
89 // before calling BatchCompleteItems on the main thread
91 
92 // Number of items that need to be added for us to run ANALYZE automatically.
93 const PRUint32 NUM_ITEMS_PROCESSED_THRESHOLD = 1000;
94 
95 typedef sbStringSet::iterator sbStringSetIter;
96 
97 #include "prlog.h"
98 #ifdef PR_LOGGING
99 extern PRLogModuleInfo* gMetadataLog;
100 #define TRACE(args) PR_LOG(gMetadataLog, PR_LOG_DEBUG, args)
101 #define LOG(args) PR_LOG(gMetadataLog, PR_LOG_WARN, args)
102 #ifdef __GNUC__
103 #define __FUNCTION__ __PRETTY_FUNCTION__
104 #endif
105 #else
106 #define TRACE(args) /* nothing */
107 #define LOG(args) /* nothing */
108 #endif
109 
110 // CLASSES ====================================================================
111 
113  nsIClassInfo,
118 
120  nsIClassInfo,
125 
128 
130  mStatus(sbIJobProgress::STATUS_RUNNING),
131  mBlocked(PR_FALSE),
132  mCompletedItemCount(0),
133  mTotalItemCount(0),
134  mJobType(TYPE_READ),
135  mLibrary(nsnull),
136  mRequiredProperties(nsnull),
137  mNextMainThreadIndex(0),
138  mNextBackgroundThreadIndex(0),
139  mBackgroundItemsLock(nsnull),
140  mProcessedBackgroundThreadItems(nsnull),
141  mProcessedBackgroundItemsLock(nsnull),
142  mInLibraryBatch(PR_FALSE),
143  mStringBundle(nsnull)
144 {
145  TRACE(("%s[%.8x]", __FUNCTION__, this));
146  MOZ_COUNT_CTOR(sbMetadataJob);
147 }
148 
150 {
151  TRACE(("%s[%.8x]", __FUNCTION__, this));
152  MOZ_COUNT_DTOR(sbMetadataJob);
153 
154  // Make extra sure we aren't in a library batch
155  EndLibraryBatch();
156 
157  if (mBackgroundItemsLock) {
158  nsAutoLock::DestroyLock(mBackgroundItemsLock);
159  }
160  if (mProcessedBackgroundItemsLock) {
161  nsAutoLock::DestroyLock(mProcessedBackgroundItemsLock);
162  }
163 }
164 
165 
166 nsresult sbMetadataJob::Init(nsIArray *aMediaItemsArray,
167  nsIStringEnumerator* aRequiredProperties,
168  JobType aJobType)
169 {
170  TRACE(("%s[%.8x]", __FUNCTION__, this));
171  NS_ENSURE_ARG_POINTER(aMediaItemsArray);
172  NS_ASSERTION(NS_IsMainThread(), \
173  "sbMetadataJob::Init: MUST NOT INIT OFF OF MAIN THREAD!");
174  nsresult rv;
175 
176  NS_ENSURE_FALSE(mBackgroundItemsLock, NS_ERROR_ALREADY_INITIALIZED);
177  mBackgroundItemsLock = nsAutoLock::NewLock(
178  "sbMetadataJob background item lock");
179  NS_ENSURE_TRUE(mBackgroundItemsLock, NS_ERROR_OUT_OF_MEMORY);
180 
181  NS_ENSURE_FALSE(mProcessedBackgroundItemsLock, NS_ERROR_ALREADY_INITIALIZED);
182  mProcessedBackgroundItemsLock = nsAutoLock::NewLock(
183  "sbMetadataJob processed background items lock");
184  NS_ENSURE_TRUE(mProcessedBackgroundItemsLock, NS_ERROR_OUT_OF_MEMORY);
185 
186  // Find the library for the items in the array.
187  PRUint32 length;
188  rv = aMediaItemsArray->GetLength(&length);
189  NS_ENSURE_SUCCESS(rv, rv);
190  NS_ENSURE_TRUE(length > 0, NS_ERROR_INVALID_ARG);
191 
192  nsCOMPtr<sbIMediaItem> mediaItem =
193  do_QueryElementAt(aMediaItemsArray, 0, &rv);
194  NS_ENSURE_SUCCESS(rv, rv);
195 
196  rv = mediaItem->GetLibrary(getter_AddRefs(mLibrary));
197  NS_ENSURE_SUCCESS(rv, rv);
198 
199  mJobType = aJobType;
200 
201  // Check if we have to remove any properties due to preferences
202  if (mJobType == TYPE_WRITE) {
203  NS_ENSURE_ARG_POINTER(aRequiredProperties);
204 
205  // Create a string array of the properties
206  PRBool hasMore;
207  rv = aRequiredProperties->HasMore(&hasMore);
208  NS_ENSURE_SUCCESS(rv, rv);
209 
210  nsString propertyId;
211  while (hasMore) {
212  rv = aRequiredProperties->GetNext(propertyId);
213  NS_ENSURE_SUCCESS(rv, rv);
214 
215  mRequiredProperties.AppendString(propertyId);
216 
217  rv = aRequiredProperties->HasMore(&hasMore);
218  NS_ENSURE_SUCCESS(rv, rv);
219  }
220 
221  // Figure out if we are allowed to write rating or artwork values to files
222  PRBool enableRatingWrite = PR_FALSE;
223  PRBool enableArtworkWrite = PR_FALSE;
224  nsCOMPtr<nsIPrefBranch> prefService =
225  do_GetService("@mozilla.org/preferences-service;1", &rv);
226  NS_ENSURE_SUCCESS( rv, rv);
227  prefService->GetBoolPref("songbird.metadata.ratings.enableWriting",
228  &enableRatingWrite);
229  prefService->GetBoolPref("songbird.metadata.artwork.enableWriting",
230  &enableArtworkWrite);
231 
232  if (!enableRatingWrite) {
233  // Remove the rating property if we are not allowed to write it out
234  mRequiredProperties.RemoveString(NS_LITERAL_STRING(SB_PROPERTY_RATING));
235  }
236  if (!enableArtworkWrite) {
237  // Remove the primaryImageURL property if we are not allowed to write it out
238  mRequiredProperties.RemoveString(NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL));
239  }
240  }
241 
242  rv = AppendMediaItems(aMediaItemsArray);
243  NS_ENSURE_SUCCESS(rv, rv);
244 
245  if (mBackgroundThreadJobItems.Length() > 0) {
246  BeginLibraryBatch();
247  }
248 
249  return rv;
250 }
251 
252 
253 nsresult sbMetadataJob::AppendMediaItems(nsIArray *aMediaItemsArray)
254 {
255  TRACE(("%s[%.8x]", __FUNCTION__, this));
256  NS_ENSURE_ARG_POINTER(aMediaItemsArray);
257  NS_ENSURE_STATE(mLibrary);
258  NS_ASSERTION(NS_IsMainThread(), \
259  "sbMetadataJob::AppendMediaItems is main thread only!");
260  nsresult rv;
261 
262  if (mStatus != sbIJobProgress::STATUS_RUNNING)
263  return NS_ERROR_UNEXPECTED;
264 
265  PRUint32 length;
266  rv = aMediaItemsArray->GetLength(&length);
267  NS_ENSURE_SUCCESS(rv, rv);
268  NS_ENSURE_TRUE(length > 0, NS_ERROR_INVALID_ARG);
269 
270  nsCOMPtr<sbIMediaItem> mediaItem;
271 
272  // Ensure that all the items belong to the same
273  // library. This is necessary because batch operations
274  // can only be performed on the library level.
275  // Unfortunate.
276  for (PRUint32 i=0; i < length; i++) {
277  mediaItem = do_QueryElementAt(aMediaItemsArray, i, &rv);
278  NS_ENSURE_SUCCESS(rv, rv);
279 
280  nsCOMPtr<sbILibrary> library;
281  rv = mediaItem->GetLibrary(getter_AddRefs(library));
282  NS_ENSURE_SUCCESS(rv, rv);
283 
284  PRBool equals;
285  rv = library->Equals(mLibrary, &equals);
286  NS_ENSURE_SUCCESS(rv, rv);
287 
288  if (!equals) {
289  NS_ERROR("Not all items from the same library");
290  return NS_ERROR_INVALID_ARG;
291  }
292  }
293 
294  // If this job type is a write, inform the watch folder service of the paths
295  // that are about to be written to. This prevents the watch folder service
296  // from re-reading in the changes that this job is about to do.
297  PRBool shouldIgnorePaths = PR_FALSE;
298  nsCOMPtr<sbIWatchFolderService> wfService;
299  if (mJobType == TYPE_WRITE) {
300  wfService = do_GetService("@songbirdnest.com/watch-folder-service;1", &rv);
301  if (NS_SUCCEEDED(rv) && wfService) {
302  rv = wfService->GetIsRunning(&shouldIgnorePaths);
303  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
304  "Could not determine if watchfolders is running!");
305  }
306  }
307 
308  // Now that we know the input is fine, add it to our
309  // list of things to be done
310 
311  mTotalItemCount += length;
312 
313  // We have to keep two lists, one for items that
314  // must be processed on the main thread, and one for
315  // items that can be processed on a background thread.
316 
317  // Avoid allocating both lists, since most jobs
318  // will be entirely main thread or entirely background thread
319  PRBool resizedMainThreadItems = PR_FALSE;
320  PRBool resizedBackgroundThreadItems = PR_FALSE;
321 
322  for (PRUint32 i=0; i < length; i++) {
323  mediaItem = do_QueryElementAt(aMediaItemsArray, i, &rv);
324  NS_ENSURE_SUCCESS(rv, rv);
325 
326  // If the watch folder service is running, append this path to the ignore
327  // list on the service. The path will be removed from the ignore list after
328  // the import job has completed.
329  if (shouldIgnorePaths) {
330  nsString mediaItemPath;
331  rv = mediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
332  mediaItemPath);
333  if (NS_SUCCEEDED(rv)) {
334  rv = wfService->AddIgnorePath(mediaItemPath);
335  if (NS_SUCCEEDED(rv)) {
336  mIgnoredContentPaths.insert(mediaItemPath);
337  }
338  else {
339  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
340  "Could not add a watch folder ignore path!");
341  }
342  }
343  }
344 
345  nsRefPtr<sbMetadataJobItem> jobItem =
346  new sbMetadataJobItem(mJobType, mediaItem, &mRequiredProperties, this);
347  NS_ENSURE_TRUE(jobItem, NS_ERROR_OUT_OF_MEMORY);
348 
349  // Set up the handler now, as this must be done on the main thread
350  // and only the handler knows whether it can be processed
351  // in the background.
352  rv = SetUpHandlerForJobItem(jobItem);
353 
354  if (NS_FAILED(rv)) {
355  // If we couldn't get a handler, instant FAIL
356  HandleFailedItem(jobItem);
357  mCompletedItemCount++;
358  } else {
359 
360  // Find out if the handler can be run off the main thread
361  PRBool requiresMainThread = PR_TRUE;
362  nsCOMPtr<sbIMetadataHandler> handler;
363  rv = jobItem->GetHandler(getter_AddRefs(handler));
364  NS_ENSURE_SUCCESS(rv, rv);
365  rv = handler->GetRequiresMainThread(&requiresMainThread);
366  NS_ASSERTION(NS_SUCCEEDED(rv), \
367  "sbMetadataJob::AppendMediaItems GetRequiresMainThread failed");
368 
369  // Separate the list of items based on main thread requirements.
370  if (requiresMainThread) {
371 
372  // If we haven't allocated space on the main thread queue yet,
373  // do so now. Better to waste space than append.
374  if (!resizedMainThreadItems) {
375  resizedMainThreadItems =
376  mMainThreadJobItems.SetCapacity(mTotalItemCount);
377  NS_ENSURE_TRUE(resizedMainThreadItems, NS_ERROR_OUT_OF_MEMORY);
378  }
379 
380  mMainThreadJobItems.AppendElement(jobItem);
381  } else {
382  nsAutoLock lock(mBackgroundItemsLock);
383 
384  // If we haven't allocated space on the background thread queue yet,
385  // do so now. Better to waste space than append.
386  if (!resizedBackgroundThreadItems) {
387  resizedBackgroundThreadItems =
388  mBackgroundThreadJobItems.SetCapacity(mTotalItemCount);
389  NS_ENSURE_TRUE(resizedBackgroundThreadItems, NS_ERROR_OUT_OF_MEMORY);
390  }
391 
392  mBackgroundThreadJobItems.AppendElement(jobItem);
393  }
394  }
395  }
396 
397  return NS_OK;
398 }
399 
400 nsresult
402 {
403  NS_ENSURE_ARG_POINTER(aJobItem);
404 
405  // Avoid allocating both lists, since most jobs
406  // will be entirely main thread or entirely background thread
407  PRBool resizedMainThreadItems = PR_FALSE;
408  PRBool resizedBackgroundThreadItems = PR_FALSE;
409 
410  // Find out if the handler can be run off the main thread
411  PRBool requiresMainThread = PR_TRUE;
412  nsCOMPtr<sbIMetadataHandler> handler;
413  nsresult rv = aJobItem->GetHandler(getter_AddRefs(handler));
414  NS_ENSURE_SUCCESS(rv, rv);
415  rv = handler->GetRequiresMainThread(&requiresMainThread);
416  NS_ASSERTION(NS_SUCCEEDED(rv), \
417  "sbMetadataJob::AppendJobItems GetRequiresMainThread failed");
418 
419  // Separate the list of items based on main thread requirements.
420  if (requiresMainThread) {
421  // If we haven't allocated space on the main thread queue yet,
422  // do so now. Better to waste space than append.
423  if (!resizedMainThreadItems) {
424  resizedMainThreadItems =
425  mMainThreadJobItems.SetCapacity(mTotalItemCount);
426  NS_ENSURE_TRUE(resizedMainThreadItems, NS_ERROR_OUT_OF_MEMORY);
427  }
428 
429  mMainThreadJobItems.AppendElement(aJobItem);
430  } else {
431  nsAutoLock lock(mBackgroundItemsLock);
432 
433  // If we haven't allocated space on the background thread queue yet,
434  // do so now. Better to waste space than append.
435  if (!resizedBackgroundThreadItems) {
436  resizedBackgroundThreadItems =
437  mBackgroundThreadJobItems.SetCapacity(mTotalItemCount);
438  NS_ENSURE_TRUE(resizedBackgroundThreadItems, NS_ERROR_OUT_OF_MEMORY);
439  }
440 
441  mBackgroundThreadJobItems.AppendElement(aJobItem);
442  }
443 
444  mTotalItemCount++;
445 
446  return NS_OK;
447 }
448 
449 nsresult
450 sbMetadataJob::SetUpHandlerForJobItem(sbMetadataJobItem* aJobItem)
451 {
452  TRACE(("%s[%.8x]", __FUNCTION__, this));
453  NS_ENSURE_ARG_POINTER(aJobItem);
454  NS_ASSERTION(NS_IsMainThread(), \
455  "sbMetadataJob::SetUpHandlerForJobItem is main thread only!");
456  nsresult rv;
457 
458  // First lets get the URL for this media item
459  nsCOMPtr<sbIMediaItem> mediaItem;
460  rv = aJobItem->GetMediaItem(getter_AddRefs(mediaItem));
461  NS_ENSURE_SUCCESS(rv, rv);
462  nsString stringURL;
463  rv = mediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
464  stringURL);
465  NS_ENSURE_SUCCESS(rv, rv);
466 
467  rv = aJobItem->SetURL(NS_ConvertUTF16toUTF8(stringURL));
468  NS_ENSURE_SUCCESS(rv, rv);
469 
470  // Now see if we can find a handler for this URL
471  nsCOMPtr<sbIMetadataManager> metadataManager =
472  do_GetService("@songbirdnest.com/Songbird/MetadataManager;1", &rv);
473  NS_ENSURE_SUCCESS(rv, rv);
474  nsCOMPtr<sbIMetadataHandler> handler;
475  rv = metadataManager->GetHandlerForMediaURL(stringURL,
476  getter_AddRefs(handler));
477 
478  if (rv == NS_ERROR_UNEXPECTED) {
479  // No handler found, so try getting a handler for originURL if available
480  rv = mediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL),
481  stringURL);
482  NS_ENSURE_SUCCESS(rv, rv);
483  if (stringURL.IsEmpty()) {
484  // No origin URL, too bad - restore the error
485  rv = NS_ERROR_UNEXPECTED;
486  } else {
487  // Only use origin url if it's a local file.
488  // Unfortunately, we can't touch the IO service since this can run on
489  // a background thread.
490  if (StringBeginsWith(stringURL, NS_LITERAL_STRING("file://"))) {
491  // TODO GetFileSize will fail, since it wont use this URL
492  rv = metadataManager->GetHandlerForMediaURL(stringURL,
493  getter_AddRefs(handler));
494  } else {
495  // Nope, can't use non-file origin url
496  rv = NS_ERROR_UNEXPECTED;
497  }
498  }
499  }
500  #if PR_LOGGING
501  if (NS_FAILED(rv)) {
502  LOG(("Failed to find metadata handler\n"));
503  }
504  #endif /* PR_LOGGING */
505  NS_ENSURE_SUCCESS(rv, rv);
506 
507  // Finally, stick the handler into the jobitem
508  rv = aJobItem->SetHandler(handler);
509  NS_ENSURE_SUCCESS(rv, rv);
510 
511  return rv;
512 }
513 
514 
515 nsresult sbMetadataJob::GetQueuedItem(PRBool aMainThreadOnly,
516  sbMetadataJobItem** aJobItem)
517 {
518  TRACE(("%s[%.8x]", __FUNCTION__, this));
519  NS_ENSURE_ARG_POINTER(aJobItem);
520  nsresult rv;
521 
522  // Make sure the job is still running
523  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
524  TRACE(("%s[%.8x]: not running", __FUNCTION__, this));
525  return NS_ERROR_NOT_AVAILABLE;
526  }
527 
528  nsRefPtr<sbMetadataJobItem> item = nsnull;
529 
530  if (aMainThreadOnly) {
531  // Nobody should be requesting main thread only items off of
532  // the main thread
533  NS_ASSERTION(NS_IsMainThread(), \
534  "sbMetadataJob::GetQueuedItem: main thread item off main thread!");
535 
536  if (mNextMainThreadIndex < mMainThreadJobItems.Length()) {
537  mMainThreadJobItems[mNextMainThreadIndex++].swap(item);
538  } else {
539  TRACE(("%s[%.8x] - NOT_AVAILABLE (%i/%i)\n",
540  __FUNCTION__, this,
541  mNextMainThreadIndex, mMainThreadJobItems.Length()));
542  return NS_ERROR_NOT_AVAILABLE;
543  }
544  } else {
545  // Background list must be locked, as theoretically
546  // more items could be appended (though in the current impl
547  // this does not happen), and because GetStatusText
548  // needs to access it to find a file name.
549  nsAutoLock lock(mBackgroundItemsLock);
550 
551  if (mNextBackgroundThreadIndex < mBackgroundThreadJobItems.Length()) {
552  mBackgroundThreadJobItems[mNextBackgroundThreadIndex++].swap(item);
553  } else {
554  TRACE(("%s[%.8x] - background NOT_AVAILABLE (%i/%i)\n",
555  __FUNCTION__, this,
556  mNextBackgroundThreadIndex, mBackgroundThreadJobItems.Length()));
557  return NS_ERROR_NOT_AVAILABLE;
558  }
559  }
560  NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
561 
562  // If this is a write job, we need to prepare the
563  // list of properties that are to be written
564  if (mJobType == TYPE_WRITE) {
565  rv = PrepareWriteItem(item);
566 
567  if (!NS_SUCCEEDED(rv)) {
568  NS_ERROR("sbMetadataJob::GetQueuedItem failed to prepare a "
569  "job item for writing.");
570  PutProcessedItem(item);
571  }
572  NS_ENSURE_SUCCESS(rv, rv);
573  }
574 
575  TRACE(("%s[%.8x]: got %.8x", __FUNCTION__, this, item.get()));
576  item.forget(aJobItem);
577  return NS_OK;
578 }
579 
580 
582 {
583  NS_ENSURE_ARG_POINTER(aJobItem);
584  TRACE(("%s[%.8x]", __FUNCTION__, this));
585 
586  // Make sure the job is still running
587  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
588  LOG(("%s[%.8x] ignored. Job canceled\n", __FUNCTION__, this));
589  return NS_OK;
590  }
591 
592  // If we're being passed an item on the main thread, just complete
593  // it immediately
594  if (NS_IsMainThread()) {
595  HandleProcessedItem(aJobItem);
596  } else {
597  // On a background thread, so can't complete the item just yet.
598  DeferProcessedItemHandling(aJobItem);
599  }
600  return NS_OK;
601 }
602 
603 
604 nsresult sbMetadataJob::PrepareWriteItem(sbMetadataJobItem *aJobItem)
605 {
606  TRACE(("%s[%.8x]", __FUNCTION__, this));
607  NS_ENSURE_ARG_POINTER(aJobItem);
608  NS_ASSERTION(mJobType == TYPE_WRITE, \
609  "sbMetadataJob::PrepareWriteItem called during a read job");
610  nsresult rv;
611 
612  // Get the properties from the meta data job item
613  nsCOMPtr<sbIMutablePropertyArray> writeProps;
614  rv = aJobItem->GetProperties(getter_AddRefs(writeProps));
615  NS_ENSURE_SUCCESS(rv, rv);
616 
617  nsCOMPtr<sbIMetadataHandler> handler;
618  rv = aJobItem->GetHandler(getter_AddRefs(handler));
619  NS_ENSURE_SUCCESS(rv, rv);
620 
621  // Set properties on the handler
622  rv = handler->SetProps(writeProps);
623  NS_ENSURE_SUCCESS(rv, rv);
624 
625  return rv;
626 }
627 
628 
629 nsresult sbMetadataJob::HandleProcessedItem(sbMetadataJobItem *aJobItem)
630 {
631  TRACE(("%s[%.8x]", __FUNCTION__, this));
632  NS_ENSURE_ARG_POINTER(aJobItem);
633  NS_ASSERTION(NS_IsMainThread(), \
634  "sbMetadataJob::HandleProcessedItem is main thread only!");
635  nsresult rv;
636 
637  mCompletedItemCount++;
638 
639  // For read items, filter properties from the handler
640  // and set them on the media item
641  if (mJobType == TYPE_READ) {
642  PRBool willRetry = PR_FALSE;
643  rv = CopyPropertiesToMediaItem(aJobItem, &willRetry);
644  NS_ASSERTION(NS_SUCCEEDED(rv), \
645  "sbMetadataJob::HandleProcessedItem CopyPropertiesToMediaItem failed!");
646 
647  // Going to retry, return now.
648  if(willRetry) {
649  return NS_OK;
650  }
651  } else {
652  // For write items, we need to check for failure. Also, we need to
653  // update the content-length property if we did write to the file.
654  PRBool processed = PR_FALSE;
655  aJobItem->GetProcessed(&processed);
656  if (!processed) {
657  rv = HandleFailedItem(aJobItem);
658  NS_ASSERTION(NS_SUCCEEDED(rv), \
659  "sbMetadataJob::HandleProcessedItem HandleFailedItem failed!");
660  }
661 
662  rv = HandleWrittenItem(aJobItem);
663  }
664 
665  // Close the handler
666  nsCOMPtr<sbIMetadataHandler> handler;
667  rv = aJobItem->GetHandler(getter_AddRefs(handler));
668  NS_ENSURE_SUCCESS(rv, rv);
669  rv = handler->Close();
670  NS_ENSURE_SUCCESS(rv, rv);
671  return rv;
672 }
673 
674 nsresult sbMetadataJob::HandleWrittenItem(sbMetadataJobItem *aJobItem)
675 {
676  TRACE(("%s[%.8x]", __FUNCTION__, this));
677  nsresult rv;
678 
679  nsCOMPtr<sbIMediaItem> item;
680  rv = aJobItem->GetMediaItem(getter_AddRefs(item));
681  NS_ENSURE_SUCCESS(rv, rv);
682 
683  nsCOMPtr<nsIURI> contentURI;
684  rv = item->GetContentSrc(getter_AddRefs(contentURI));
685  NS_ENSURE_SUCCESS(rv, rv);
686 
687  /* If it's not a file, that's ok */
688  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(contentURI, &rv);
689  if (NS_FAILED (rv))
690  return NS_OK;
691 
692  nsCOMPtr<nsIFile> file;
693  rv = fileURL->GetFile(getter_AddRefs(file));
694  NS_ENSURE_SUCCESS(rv, rv);
695 
696  PRInt64 fileSize;
697  rv = file->GetFileSize(&fileSize);
698  NS_ENSURE_SUCCESS(rv, rv);
699 
700  nsString strContentLength;
701  AppendInt(strContentLength, fileSize);
702 
703  rv = item->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTLENGTH),
704  strContentLength);
705  NS_ENSURE_SUCCESS(rv, rv);
706 
707  return NS_OK;
708 }
709 
710 nsresult sbMetadataJob::DeferProcessedItemHandling(sbMetadataJobItem *aJobItem)
711 {
712  TRACE(("%s[%.8x]", __FUNCTION__, this));
713  NS_ENSURE_ARG_POINTER(aJobItem);
714 
715  // Even if there is no other work to do, everything
716  // needs to be completed on the main thread
717  // (may release nsStandardURLs, etc.)
718 
719  nsAutoLock lock(mProcessedBackgroundItemsLock);
720 
721  // Make sure the job is still running.
722  // We do this inside the lock, since Cancel() will
723  // lock the array before updating the status, and we
724  // don't want a race condition.
725  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
726  LOG(("%s[%.8x] - Job canceled\n", __FUNCTION__, this));
727  return NS_OK;
728  }
729 
730  // Make sure we've got somewhere to put the items
731  if (!mProcessedBackgroundThreadItems) {
732  mProcessedBackgroundThreadItems =
733  new nsTArray<nsRefPtr<sbMetadataJobItem> >(
735  }
736 
737  // Save it for later
738  mProcessedBackgroundThreadItems->AppendElement(aJobItem);
739 
740  return NS_OK;
741 }
742 
743 
744 nsresult sbMetadataJob::CopyPropertiesToMediaItem(sbMetadataJobItem *aJobItem,
745  PRBool* aWillRetry)
746 {
747  TRACE(("%s[%.8x]", __FUNCTION__, this));
748  NS_ENSURE_ARG_POINTER(aJobItem);
749  NS_ENSURE_ARG_POINTER(aWillRetry);
750  NS_ASSERTION(NS_IsMainThread(), \
751  "sbMetadataJob::CopyPropertiesToMediaItem is main thread only!");
752  nsresult rv;
753 
754  // Get the sbIMediaItem we're supposed to be updating
755  nsCOMPtr<sbIMediaItem> item;
756  rv = aJobItem->GetMediaItem(getter_AddRefs(item));
757  NS_ENSURE_SUCCESS(rv, rv);
758 
759  // Get a new array we're going to copy across
760  nsCOMPtr<sbIMutablePropertyArray> newProps =
761  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
762  NS_ENSURE_SUCCESS(rv, rv);
763 
764  NS_NAMED_LITERAL_STRING( trackNameKey, SB_PROPERTY_TRACKNAME );
765  NS_NAMED_LITERAL_STRING( contentTypeKey, SB_PROPERTY_CONTENTTYPE );
766  nsAutoString oldName;
767  rv = item->GetProperty( trackNameKey, oldName );
768  nsAutoString trackName;
769 
770  // Get the metadata properties that were found
771  nsCOMPtr<sbIMetadataHandler> handler;
772  rv = aJobItem->GetHandler(getter_AddRefs(handler));
773  NS_ENSURE_SUCCESS(rv, rv);
774 
775  nsCOMPtr<sbIMutablePropertyArray> props;
776  PRUint32 propsLength = 0;
777  rv = handler->GetProps(getter_AddRefs(props));
778 
779  // If we found properties, check to see if
780  // we got a track name
781  if (NS_SUCCEEDED(rv)) {
782  NS_ENSURE_TRUE(props, NS_ERROR_UNEXPECTED);
783  rv = props->GetLength(&propsLength);
784  NS_ENSURE_SUCCESS(rv, rv);
785  rv = props->GetPropertyValue( trackNameKey, trackName );
786 
787  if (NS_FAILED(rv)) {
788  // If we didn't get a track name, that's usually a sign that something
789  // went wrong in metadata scan, and we should fall through and try the
790  // next handler in case it works better. However, for video items, it's
791  // very common to not have title metadata - so we don't do this if the
792  // content type is video.
793  nsAutoString contentType;
794  rv = props->GetPropertyValue( contentTypeKey, contentType );
795 
796  // So now only go through the retry path if we couldn't get the content
797  // type, or it wasn't video.
798  if (NS_FAILED(rv) || !contentType.EqualsLiteral("video")) {
799  rv = HandleFailedItem(aJobItem, PR_TRUE, aWillRetry);
800  NS_ENSURE_SUCCESS(rv, rv);
801  }
802  }
803  } else {
804  rv = HandleFailedItem(aJobItem, PR_TRUE, aWillRetry);
805  NS_ENSURE_SUCCESS(rv, rv);
806  }
807 
808  // Will retry with another handler. Bail out now.
809  if(*aWillRetry == PR_TRUE) {
810  return NS_OK;
811  }
812 
813  // Get the property manager because we love it so much
814  nsCOMPtr<sbIPropertyManager> propMan =
815  do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
816  NS_ENSURE_SUCCESS(rv, rv);
817 
818  // Update the title if needed
819  PRBool defaultTrackname = trackName.IsEmpty() && !oldName.IsEmpty();
820  // If the metadata read can't even find a song name,
821  // AND THERE ISN'T ALREADY A TRACK NAME, cook one up off the url.
822  if ( trackName.IsEmpty() && oldName.IsEmpty() ) {
823  rv = CreateDefaultItemName(item, trackName);
824  NS_ENSURE_SUCCESS(rv, rv);
825  // And then add/change it
826  if ( !trackName.IsEmpty() ) {
827  rv = AppendToPropertiesIfValid( propMan, newProps, trackNameKey, trackName );
828  NS_ENSURE_SUCCESS(rv, rv);
829  defaultTrackname = PR_TRUE;
830  }
831  }
832 
833  // Loop through the returned props to copy to the new props
834  for (PRUint32 i = 0; i < propsLength && NS_SUCCEEDED(rv); i++) {
835  nsCOMPtr<sbIProperty> prop;
836  rv = props->GetPropertyAt( i, getter_AddRefs(prop) );
837  if (NS_FAILED(rv))
838  break;
839  nsAutoString id, value;
840  prop->GetId( id );
841  if (!defaultTrackname || !id.Equals(trackNameKey)) {
842  prop->GetValue( value );
843  if (!value.IsEmpty() && !value.IsVoid() && !value.EqualsLiteral(" ")) {
844  AppendToPropertiesIfValid( propMan, newProps, id, value );
845  }
846  }
847  }
848 
849  PRBool isLocalFile = PR_FALSE;
850 
851  PRInt64 fileSize = 0;
852  rv = GetFileSize(item, &fileSize);
853  if (NS_SUCCEEDED(rv)) {
854  nsAutoString contentLength;
855  AppendInt(contentLength, fileSize);
856  rv = AppendToPropertiesIfValid(propMan,
857  newProps,
858  NS_LITERAL_STRING(SB_PROPERTY_CONTENTLENGTH),
859  contentLength);
860  NS_ENSURE_SUCCESS(rv, rv);
861 
862  isLocalFile = PR_TRUE;
863  }
864 
865  rv = item->SetProperties(newProps);
866  NS_ENSURE_SUCCESS(rv, rv);
867 
868  if (isLocalFile){
869  // For local files we want to trigger an album art lookup.
870  // Do this AFTER setting properties, since the fetchers
871  // may make use of the metadata.
872  rv = ReadAlbumArtwork(aJobItem);
873  NS_ASSERTION(NS_SUCCEEDED(rv),
874  "Metadata job failed to run album art fetcher");
875  }
876 
877  return NS_OK;
878 }
879 
880 //------------------------------------------------------------------------------
881 //
882 // sbIAlbumArtListner Implementation.
883 //
884 //------------------------------------------------------------------------------
885 
886 /* onChangeFetcher(in sbIAlbumArtFetcher aFetcher); */
887 NS_IMETHODIMP sbMetadataJob::OnChangeFetcher(sbIAlbumArtFetcher* aFetcher)
888 {
889  TRACE(("%s[%.8x]", __FUNCTION__, this));
890  // Ignoring this, it should only be metadata fetching by default
891  return NS_OK;
892 }
893 
894 /* onTrackResult(in nsIURI aImageLocation, in sbIMediaItem aMediaItem); */
895 NS_IMETHODIMP sbMetadataJob::OnTrackResult(nsIURI* aImageLocation,
896  sbIMediaItem* aMediaItem)
897 {
898  TRACE(("%s[%.8x]", __FUNCTION__, this));
899  // Validate arguments.
900  NS_ENSURE_ARG_POINTER(aMediaItem);
901 
902  if (aImageLocation) {
903  nsresult rv;
904  nsCAutoString imageFileURISpec;
905  rv = aImageLocation->GetSpec(imageFileURISpec);
906  if (NS_SUCCEEDED(rv)) {
907  rv = aMediaItem->SetProperty(
908  NS_LITERAL_STRING(SB_PROPERTY_PRIMARYIMAGEURL),
909  NS_ConvertUTF8toUTF16(imageFileURISpec));
910  NS_ENSURE_SUCCESS(rv, rv);
911  }
912  }
913 
914  return NS_OK;
915 }
916 
917 /* onAlbumResult(in nsIURI aImageLocation, in nsIArray aMediaItems); */
918 NS_IMETHODIMP sbMetadataJob::OnAlbumResult(nsIURI* aImageLocation,
919  nsIArray* aMediaItems)
920 {
921  TRACE(("%s[%.8x]", __FUNCTION__, this));
922  // We only called FetchAlbumArtForTrack so we should not get this.
923  return NS_OK;
924 }
925 
926 /* onSearchComplete(in nsIArray aMediaItems); */
927 NS_IMETHODIMP sbMetadataJob::OnSearchComplete(nsIArray* aMediaItems)
928 {
929  TRACE(("%s[%.8x]", __FUNCTION__, this));
930  // We don't write back here since we are in the import and don't want to take
931  // to much time.
932  return NS_OK;
933 }
934 
935 nsresult sbMetadataJob::ReadAlbumArtwork(sbMetadataJobItem *aJobItem)
936 {
937  TRACE(("%s[%.8x]", __FUNCTION__, this));
938  NS_ENSURE_ARG_POINTER(aJobItem);
939  nsresult rv;
940 
941  if (!mArtFetcher) {
942  mArtFetcher =
943  do_CreateInstance("@songbirdnest.com/Songbird/album-art-fetcher-set;1", &rv);
944  NS_ENSURE_SUCCESS(rv, rv);
945  rv = mArtFetcher->SetFetcherType(sbIAlbumArtFetcherSet::TYPE_LOCAL);
946  NS_ENSURE_SUCCESS(rv, rv);
947  }
948 
949  // Recycle the existing handler to avoid re-reading the file
950  nsCOMPtr<nsIMutableArray> sources =
951  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
952  NS_ENSURE_SUCCESS(rv, rv);
953  nsCOMPtr<sbIMetadataHandler> handler;
954  rv = aJobItem->GetHandler(getter_AddRefs(handler));
955  NS_ENSURE_SUCCESS(rv, rv);
956  rv = sources->AppendElement(handler, PR_FALSE);
957  NS_ENSURE_SUCCESS(rv, rv);
958  rv = mArtFetcher->SetAlbumArtSourceList(sources);
959  NS_ENSURE_SUCCESS(rv, rv);
960 
961  // Attempt to fetch some art
962  nsCOMPtr<sbIMediaItem> item;
963  rv = aJobItem->GetMediaItem(getter_AddRefs(item));
964  NS_ENSURE_SUCCESS(rv, rv);
965  rv = mArtFetcher->FetchAlbumArtForTrack(item, this);
966  NS_ENSURE_SUCCESS(rv, rv);
967 
968  TRACE(("%s[%.8x] - finished rv %08x\n", __FUNCTION__, this, rv));
969 
970  return NS_OK;
971 }
972 
973 
974 nsresult sbMetadataJob::HandleFailedItem(sbMetadataJobItem *aJobItem,
975  PRBool aShouldRetry /*= PR_FALSE*/,
976  PRBool *aWillRetry /*= nsnull*/)
977 {
978  TRACE(("%s[%.8x]", __FUNCTION__, this));
979  NS_ENSURE_ARG_POINTER(aJobItem);
980  NS_ASSERTION(NS_IsMainThread(), \
981  "sbMetadataJob::HandleFailedItem is main thread only!");
982  nsresult rv;
983 
984  // Default to no retry.
985  if (aShouldRetry && aWillRetry)
986  {
987  TRACE(("%s[%.8x] - Attempting to retry job item.", __FUNCTION__, this));
988  *aWillRetry = PR_FALSE;
989  // Attempt to get next available Metadata Handler. If a handler
990  // is available resubmit the jobItem with the new handler.
991  nsCOMPtr<sbIMetadataHandler> previousHandler;
992  rv = aJobItem->GetHandler(getter_AddRefs(previousHandler));
993  // If this fails we just wrap up instead of having a hard failure.
994  // This is in a while loop so we can break out. I chose while() so that
995  // i could have my conditional check at the beginning without an extra if().
996  while (NS_SUCCEEDED(rv)) {
997  nsCOMPtr<sbIMetadataManager> metadataManager =
998  do_GetService("@songbirdnest.com/Songbird/MetadataManager;1", &rv);
999  if (NS_FAILED(rv)) {
1000  break;
1001  }
1002 
1003  nsCString stringURL;
1004  rv = aJobItem->GetURL(stringURL);
1005  if (NS_FAILED(rv)) {
1006  break;
1007  }
1008 
1009  NS_ConvertUTF8toUTF16 unicodeURL(stringURL);
1010  nsCOMPtr<sbIMetadataHandler> handler;
1011  rv = metadataManager->GetNextHandlerForMediaURL(previousHandler,
1012  unicodeURL,
1013  getter_AddRefs(handler));
1014  if(NS_FAILED(rv) || !handler) {
1015  break;
1016  }
1017 
1018  // None of these methods ever fail.
1019  aJobItem->SetProcessingStarted(PR_FALSE);
1020  aJobItem->SetProcessed(PR_FALSE);
1021  aJobItem->SetHandler(handler);
1022 
1023  // Resubmit.
1024  rv = AppendJobItem(aJobItem);
1025 
1026  // Resubmitted. Return.
1027  if (NS_SUCCEEDED(rv)) {
1028  *aWillRetry = PR_TRUE;
1029 
1030  // Restart FileMetadataService processors to ensure that our resubmitted
1031  // job item gets processed. If we do not do this it could sit in the queue
1032  // forever waiting for the main thread or background thread processors
1033  // to process it.
1034  nsCOMPtr<sbIFileMetadataService> fileMetadataService =
1035  do_GetService("@songbirdnest.com/Songbird/FileMetadataService;1", &rv);
1036  if (NS_FAILED(rv)) {
1037  break;
1038  }
1039 
1040  rv = fileMetadataService->RestartProcessors(
1043 
1044  if (NS_FAILED(rv)) {
1045  break;
1046  }
1047 
1048  rv = previousHandler->Close();
1049  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to clean up handler!");
1050 
1051  TRACE(("%s[%.8x] Retrying job item %s",
1052  __FUNCTION__, this, stringURL.BeginReading()));
1053 
1054  return NS_OK;
1055  }
1056  }
1057  }
1058 
1059  // Get the content URI as escaped UTF8
1060  nsCAutoString escapedURI, unescapedURI;
1061  rv = aJobItem->GetURL(escapedURI);
1062  NS_ENSURE_SUCCESS(rv, rv);
1063 
1064  // Now unescape it
1065  nsCOMPtr<nsINetUtil> netUtil =
1066  do_GetService("@mozilla.org/network/util;1", &rv);
1067  NS_ENSURE_SUCCESS(rv, rv);
1068  rv = netUtil->UnescapeString(escapedURI,
1069  nsINetUtil::ESCAPE_ALL,
1070  unescapedURI);
1071  NS_ENSURE_SUCCESS(rv, rv);
1072  nsString stringURL = NS_ConvertUTF8toUTF16(unescapedURI);
1073 
1074  // No need to lock, since errors are always accessed on the main
1075  // thread.
1076 
1077  // Now add the final string into the list of error messages
1078  mErrorMessages.AppendElement(stringURL);
1079 
1080  // If this is a read job, then failed items should get filename
1081  // as the trackname (this avoids blank rows, and makes it easier
1082  // to tell what failed)
1083  if (mJobType == TYPE_READ) {
1084  PRInt32 slash = stringURL.RFind(NS_LITERAL_STRING("/"));
1085  if (slash > 0 && slash < (PRInt32)(stringURL.Length() - 1)) {
1086  stringURL = nsDependentSubstring(stringURL, slash + 1,
1087  stringURL.Length() - slash - 1);
1088  }
1089 
1090  nsCOMPtr<sbIMediaItem> item;
1091  rv = aJobItem->GetMediaItem(getter_AddRefs(item));
1092  NS_ENSURE_SUCCESS(rv, rv);
1093 
1094  rv = item->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_TRACKNAME), stringURL);
1095  NS_ENSURE_SUCCESS(rv, rv);
1096  }
1097 
1098  return NS_OK;
1099 }
1100 
1101 
1102 nsresult sbMetadataJob::BatchCompleteItems()
1103 {
1104  TRACE(("%s[%.8x]", __FUNCTION__, this));
1105  NS_ASSERTION(NS_IsMainThread(), \
1106  "sbMetadataJob::BatchCompleteItems is main thread only!");
1107  nsresult rv = NS_OK;
1108 
1109  // Are there items that need completing
1110  PRBool needBatchComplete = PR_FALSE;
1111  {
1112  nsAutoLock processedLock(mProcessedBackgroundItemsLock);
1113  if (mProcessedBackgroundThreadItems) {
1114  // If the list is full, or if we're finished
1115  // and still have a few things left to complete...
1116 
1117  if (mProcessedBackgroundThreadItems->Length() >
1119  {
1120  needBatchComplete = PR_TRUE;
1121  } else {
1122  // If there is nothing left in the waiting background
1123  // item list and there are items in the incoming list,
1124  // then process right away. This is to handle
1125  // the end of the job.
1126  // Note must grab locks in the same order as Cancel()
1127  nsAutoLock waitingLock(mBackgroundItemsLock);
1128  if (mNextBackgroundThreadIndex > (mBackgroundThreadJobItems.Length() - 1) &&
1129  mProcessedBackgroundThreadItems->Length() > 0)
1130  {
1131  needBatchComplete = PR_TRUE;
1132  }
1133  }
1134  }
1135  }
1136  // If so, complete them all in a single batch
1137  if (needBatchComplete) {
1138 
1139  nsCOMPtr<sbIMediaListBatchCallback> batchCallback =
1140  new sbMediaListBatchCallback(&sbMetadataJob::RunLibraryBatch);
1141  NS_ENSURE_TRUE(batchCallback, NS_ERROR_OUT_OF_MEMORY);
1142 
1143  // If we are inside one mega-batch, just call complete directly.
1144  if (mInLibraryBatch) {
1145  rv = BatchCompleteItemsCallback();
1146  } else {
1147  // Otherwise run a small library batch
1148  rv = mLibrary->RunInBatchMode(batchCallback, static_cast<sbIJobProgress*>(this));
1149  }
1150 
1151  NS_ENSURE_SUCCESS(rv, rv);
1152  }
1153  return rv;
1154 }
1155 
1156 /* static */
1157 nsresult sbMetadataJob::RunLibraryBatch(nsISupports* aUserData)
1158 {
1159  TRACE(("%s [static]", __FUNCTION__));
1160  NS_ENSURE_ARG_POINTER(aUserData);
1161  sbMetadataJob* thisJob = static_cast<sbMetadataJob*>(
1162  static_cast<sbIJobProgress*>(aUserData));
1163  return thisJob->BatchCompleteItemsCallback();
1164 }
1165 
1166 nsresult sbMetadataJob::BatchCompleteItemsCallback()
1167 {
1168  TRACE(("%s[%.8x]", __FUNCTION__, this));
1169  nsresult rv = NS_OK;
1170 
1171  // Ok, phew, we're finally inside a library batch.
1172 
1173  // Steal the background items array and replace it with a new one.
1174  // This way we don't block the background thread from pushing
1175  // more completed items.
1176 
1177  nsAutoPtr<nsTArray<nsRefPtr<sbMetadataJobItem> > > items;
1178 
1179  { // Scope the lock
1180  nsAutoLock lock(mProcessedBackgroundItemsLock);
1181  NS_ENSURE_STATE(mProcessedBackgroundThreadItems);
1182 
1183  items = mProcessedBackgroundThreadItems.forget();
1184 
1185  // Make sure there is lots of room in the new list
1186  mProcessedBackgroundThreadItems =
1187  new nsTArray<nsRefPtr<sbMetadataJobItem> >(
1189  }
1190 
1191  // Now go complete everything.
1192  for (PRUint32 i = 0; i < items->Length(); i++) {
1193  HandleProcessedItem((*items)[i]);
1194  }
1195  return rv;
1196 }
1197 
1198 
1199 nsresult sbMetadataJob::BeginLibraryBatch()
1200 {
1201  TRACE(("%s[%.8x]", __FUNCTION__, this));
1202  NS_ENSURE_STATE(mLibrary);
1203  NS_ASSERTION(NS_IsMainThread(), \
1204  "sbMetadataJob::BeginLibraryBatch is main thread only!");
1205  nsresult rv = NS_OK;
1206 
1207  if (mInLibraryBatch) {
1208  // Already in a batch
1209  return NS_OK;
1210  }
1211 
1212  // If this is a local db library, we can get an extra
1213  // perf increase by starting a batch transaction for
1214  // the entire job.
1215  nsCOMPtr<sbILocalDatabaseLibrary> localLibrary =
1216  do_QueryInterface(mLibrary, &rv);
1217  NS_ENSURE_SUCCESS(rv,rv);
1218 
1219  localLibrary->ForceBeginUpdateBatch();
1220  mInLibraryBatch = PR_TRUE;
1221 
1222  return NS_OK;
1223 }
1224 
1225 nsresult sbMetadataJob::EndLibraryBatch()
1226 {
1227  TRACE(("%s[%.8x]", __FUNCTION__, this));
1228  NS_ENSURE_STATE(mLibrary);
1229  NS_ASSERTION(NS_IsMainThread(), \
1230  "sbMetadataJob::EndLibraryBatch is main thread only!");
1231  nsresult rv = NS_OK;
1232 
1233  if (!mInLibraryBatch) {
1234  // Nothing to do, not in a batch
1235  return NS_OK;
1236  }
1237 
1238  nsCOMPtr<sbILocalDatabaseLibrary> localLibrary =
1239  do_QueryInterface(mLibrary, &rv);
1240  NS_ENSURE_SUCCESS(rv,rv);
1241 
1242  localLibrary->ForceEndUpdateBatch();
1243  mInLibraryBatch = PR_FALSE;
1244 
1245  return NS_OK;
1246 }
1247 
1248 
1250 {
1251  TRACE(("%s[%.8x]", __FUNCTION__, this));
1252  NS_ASSERTION(NS_IsMainThread(), \
1253  "sbMetadataJob::OnJobProgress is main thread only!");
1254  nsresult rv = NS_OK;
1255 
1256  // See if there are any items needing main thread completion
1257  BatchCompleteItems();
1258 
1259  // Now figure out where we're at
1260  if (mCompletedItemCount == mTotalItemCount) {
1261  mStatus = (mErrorMessages.Length() == 0) ?
1264  }
1265 
1266  // Then announce our status to the world
1267  for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i) {
1268  mListeners[i]->OnJobProgress(this);
1269  }
1270 
1271  if (mStatus != sbIJobProgress::STATUS_RUNNING) {
1272  // We're done. No more notifications needed.
1273  mListeners.Clear();
1274 
1275  EndLibraryBatch();
1276 
1277  // If there are any paths in the ignore path set, remove them from the
1278  // watchfolders ignore whitelist.
1279  if (mIgnoredContentPaths.size() > 0) {
1280  nsCOMPtr<sbIWatchFolderService> wfService =
1281  do_GetService("@songbirdnest.com/watch-folder-service;1", &rv);
1282  NS_ASSERTION(NS_SUCCEEDED(rv),
1283  "Could not get the watch folder service!");
1284  if (NS_SUCCEEDED(rv) && wfService) {
1285  sbStringSetIter begin = mIgnoredContentPaths.begin();
1286  sbStringSetIter end = mIgnoredContentPaths.end();
1288  for (next = begin; next != end; ++next) {
1289  rv = wfService->RemoveIgnorePath(*next);
1290  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1291  "Could not remove a ignored watch folder path!");
1292  }
1293  }
1294 
1295  mIgnoredContentPaths.clear();
1296  }
1297 
1298  // Flush the library to ensure that search and filtering
1299  // work immediately.
1300  rv = mLibrary->Flush();
1301  NS_ASSERTION(NS_SUCCEEDED(rv),
1302  "sbMetadataJob::OnJobProgress failed to flush library!");
1303 
1304  //
1305  // If we've added a significant amount of items, running analyze
1306  // will ensure performance remains as good as can be. For example
1307  // adding 65,000 tracks will cause guid array queries to get all the
1308  // GUIDs to take up to 3000ms and after running analyze this same
1309  // query will run in about 600ms.
1310  //
1311  if (mCompletedItemCount > NUM_ITEMS_PROCESSED_THRESHOLD) {
1312  rv = mLibrary->Optimize(PR_TRUE);
1313  NS_ASSERTION(NS_SUCCEEDED(rv),
1314  "sbMetadataJob::onJobProgress failed to optimize library!");
1315  }
1316  }
1317  return NS_OK;
1318 }
1319 
1320 nsresult sbMetadataJob::GetType(JobType* aJobType)
1321 {
1322  TRACE(("%s[%.8x]", __FUNCTION__, this));
1323  NS_ENSURE_ARG_POINTER( aJobType );
1324  *aJobType = mJobType;
1325  return NS_OK;
1326 }
1327 
1328 nsresult sbMetadataJob::SetBlocked(PRBool aBlocked)
1329 {
1330  TRACE(("%s[%.8x]", __FUNCTION__, this));
1331 
1332  nsresult rv = NS_ERROR_UNEXPECTED;
1333 
1334  // Need to save previous state to check if we need to
1335  // begin or end the library batch.
1336  PRBool wasBlocked = mBlocked;
1337 
1338  mBlocked = aBlocked;
1339 
1340  // We may begin or end the library batch when changing blocked
1341  // states to enable other batched and non-batched operations in
1342  // the library to proceed normally. Only WRITE jobs may block.
1343  // For a particular use case why this is necessary see bug 20750.
1344 
1345  // If we were blocked and we are no longer blocked, we should
1346  // begin the library batch.
1347  if(wasBlocked && !aBlocked) {
1348  rv = BeginLibraryBatch();
1349  NS_ENSURE_SUCCESS(rv, rv);
1350  }
1351  // If we were not blocked and we are now blocked, we should
1352  // end the library batch.
1353  else if(!wasBlocked && aBlocked) {
1354  rv = EndLibraryBatch();
1355  NS_ENSURE_SUCCESS(rv, rv);
1356  }
1357 
1358  return NS_OK;
1359 }
1360 
1361 
1362 // =====================================
1363 // == sbIJobProgress Implementation ==
1364 // =====================================
1365 
1366 
1367 /* readonly attribute unsigned short status; */
1368 NS_IMETHODIMP sbMetadataJob::GetStatus(PRUint16* aStatus)
1369 {
1370  TRACE(("%s[%.8x]", __FUNCTION__, this));
1371  NS_ENSURE_ARG_POINTER( aStatus );
1372  *aStatus = mStatus;
1373  return NS_OK;
1374 }
1375 
1376 /* readonly attribute boolean blocked; */
1377 NS_IMETHODIMP sbMetadataJob::GetBlocked(PRBool* aBlocked)
1378 {
1379  TRACE(("%s[%.8x]", __FUNCTION__, this));
1380  NS_ENSURE_ARG_POINTER( aBlocked );
1381  *aBlocked = mBlocked;
1382  return NS_OK;
1383 }
1384 
1385 /* readonly attribute unsigned AString statusText; */
1386 NS_IMETHODIMP sbMetadataJob::GetStatusText(nsAString& aText)
1387 {
1388  TRACE(("%s[%.8x]", __FUNCTION__, this));
1389  NS_ASSERTION(NS_IsMainThread(), \
1390  "sbMetadataJob::GetStatusText is main thread only!");
1391  nsresult rv = NS_OK;
1392 
1393  // If the job is still running, report the leaf file name
1394  // for the next item to be processed
1395  if (mStatus == sbIJobProgress::STATUS_RUNNING) {
1396 
1397  nsCOMPtr<sbIMediaItem> mediaItem;
1398 
1399  // First check the main thread lists, since it doesn't require locking
1400  if (mNextMainThreadIndex < mMainThreadJobItems.Length()) {
1401  rv = mMainThreadJobItems[mNextMainThreadIndex]->GetMediaItem(
1402  getter_AddRefs(mediaItem));
1403  NS_ENSURE_SUCCESS(rv, rv);
1404  } else {
1405 
1406  // Ok, nothing in the main thread list, so we need to lock
1407  // and access the background list
1408  nsAutoLock lock(mBackgroundItemsLock);
1409  if (mNextBackgroundThreadIndex < mBackgroundThreadJobItems.Length()) {
1410  rv = mBackgroundThreadJobItems[mNextBackgroundThreadIndex]->
1411  GetMediaItem(getter_AddRefs(mediaItem));
1412  NS_ENSURE_SUCCESS(rv, rv);
1413  }
1414  }
1415 
1416  if (mediaItem) {
1417  // Get the unescaped file name
1418  CreateDefaultItemName(mediaItem, aText);
1419  } else {
1420  aText = mStatusText;
1421  }
1422 
1423  } else if (mStatus == sbIJobProgress::STATUS_FAILED) {
1424  // If we've failed, give a localized explanation.
1425  nsAutoString text;
1426 
1427  // Only set the status text for write jobs, since read jobs
1428  // are not currently reflected in the UI.
1429  if (mJobType == TYPE_WRITE) {
1430  const char* textKey;
1431 
1432  // Single failure in single item write job
1433  if (mTotalItemCount == 1) {
1434  textKey = "metadatajob.writing.failed.one";
1435  // Single error in multiple file write job
1436  } else if (mErrorMessages.Length() == 1) {
1437  textKey = "metadatajob.writing.failed.oneofmany";
1438  // Multiple errors in multiple file write job
1439  } else {
1440  textKey = "metadatajob.writing.failed.manyofmany";
1441  }
1442 
1443  aText = sbStringBundle().Get(textKey, "Job Failed");
1444  }
1445 
1446  } else {
1447  rv = LocalizeString(
1448  NS_LITERAL_STRING("media_scan.complete"),
1449  aText);
1450  }
1451 
1452  mStatusText = aText;
1453  return rv;
1454 }
1455 
1456 /* readonly attribute AString titleText; */
1457 NS_IMETHODIMP sbMetadataJob::GetTitleText(nsAString& aText)
1458 {
1459  TRACE(("%s[%.8x]", __FUNCTION__, this));
1460  nsresult rv;
1461 
1462  // If we haven't figured out a title yet, get one from the
1463  // string bundle.
1464  if (mTitleText.IsEmpty()) {
1465  TRACE(("sbMetadataJob::GetTitleText[0x%.8x] localizing string", this));
1466  if (mJobType == TYPE_WRITE) {
1467  rv = LocalizeString(NS_LITERAL_STRING("metadatajob.writing.title"),
1468  mTitleText);
1469  if (NS_FAILED(rv)) {
1470  mTitleText.AssignLiteral("Metadata Write Job");
1471  }
1472  } else {
1473  rv = LocalizeString(NS_LITERAL_STRING("metadatajob.reading.title"),
1474  mTitleText);
1475  if (NS_FAILED(rv)) {
1476  mTitleText.AssignLiteral("Metadata Read Job");
1477  }
1478  }
1479  }
1480 
1481  aText = mTitleText;
1482  return NS_OK;
1483 }
1484 
1485 /* readonly attribute unsigned long progress; */
1486 NS_IMETHODIMP sbMetadataJob::GetProgress(PRUint32* aProgress)
1487 {
1488  TRACE(("%s[%.8x]", __FUNCTION__, this));
1489  NS_ENSURE_ARG_POINTER( aProgress );
1490  NS_ASSERTION(NS_IsMainThread(), \
1491  "sbMetadataJob::GetProgress is main thread only!");
1492 
1493  *aProgress = mNextBackgroundThreadIndex + mNextMainThreadIndex;
1494  return NS_OK;
1495 }
1496 
1497 /* readonly attribute unsigned long total; */
1498 NS_IMETHODIMP sbMetadataJob::GetTotal(PRUint32* aTotal)
1499 {
1500  TRACE(("%s[%.8x]", __FUNCTION__, this));
1501  NS_ENSURE_ARG_POINTER( aTotal );
1502  NS_ASSERTION(NS_IsMainThread(), \
1503  "sbMetadataJob::GetTotal is main thread only!");
1504 
1505  *aTotal = mTotalItemCount;
1506  return NS_OK;
1507 }
1508 
1509 /* readonly attribute unsigned long errorCount; */
1510 NS_IMETHODIMP sbMetadataJob::GetErrorCount(PRUint32* aCount)
1511 {
1512  TRACE(("%s[%.8x]", __FUNCTION__, this));
1513  NS_ENSURE_ARG_POINTER( aCount );
1514  NS_ASSERTION(NS_IsMainThread(), \
1515  "sbMetadataJob::GetErrorCount is main thread only!");
1516 
1517  // No locking needed since mErrorMessages is only updated on
1518  // the main thread
1519  *aCount = mErrorMessages.Length();
1520  return NS_OK;
1521 }
1522 
1523 /* nsIStringEnumerator getErrorMessages(); */
1524 NS_IMETHODIMP sbMetadataJob::GetErrorMessages(nsIStringEnumerator** aMessages)
1525 {
1526  TRACE(("%s[%.8x]", __FUNCTION__, this));
1527  NS_ENSURE_ARG_POINTER(aMessages);
1528  NS_ASSERTION(NS_IsMainThread(), \
1529  "sbMetadataJob::GetProgress is main thread only!");
1530 
1531  *aMessages = nsnull;
1532 
1533  // No locking needed since mErrorMessages is only updated on
1534  // the main thread
1535  nsCOMPtr<nsIStringEnumerator> enumerator =
1536  new sbTArrayStringEnumerator(&mErrorMessages);
1537  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
1538 
1539  enumerator.forget(aMessages);
1540  return NS_OK;
1541 }
1542 
1543 /* void addJobProgressListener( in sbIJobProgressListener aListener ); */
1544 NS_IMETHODIMP
1545 sbMetadataJob::AddJobProgressListener(sbIJobProgressListener *aListener)
1546 {
1547  TRACE(("%s[%.8x]", __FUNCTION__, this));
1548  NS_ENSURE_ARG_POINTER(aListener);
1549  NS_ASSERTION(NS_IsMainThread(), \
1550  "sbMetadataJob::AddJobProgressListener is main thread only!");
1551 
1552  PRInt32 index = mListeners.IndexOf(aListener);
1553  if (index >= 0) {
1554  // the listener already exists, do not re-add
1555  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
1556  }
1557  PRBool succeeded = mListeners.AppendObject(aListener);
1558  return succeeded ? NS_OK : NS_ERROR_FAILURE;
1559 }
1560 
1561 /* void removeJobProgressListener( in sbIJobProgressListener aListener ); */
1562 NS_IMETHODIMP
1563 sbMetadataJob::RemoveJobProgressListener(sbIJobProgressListener* aListener)
1564 {
1565  TRACE(("%s[%.8x]", __FUNCTION__, this));
1566  NS_ENSURE_ARG_POINTER(aListener);
1567  NS_ASSERTION(NS_IsMainThread(), \
1568  "sbMetadataJob::RemoveJobProgressListener is main thread only!");
1569 
1570  PRInt32 indexToRemove = mListeners.IndexOf(aListener);
1571  if (indexToRemove < 0) {
1572  // Err, no such listener
1573  return NS_ERROR_UNEXPECTED;
1574  }
1575 
1576  // remove the listener
1577  PRBool succeeded = mListeners.RemoveObjectAt(indexToRemove);
1578  NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
1579 
1580  return NS_OK;
1581 }
1582 
1583 // =======================================
1584 // == sbIJobProgressUI Implementation ==
1585 // =======================================
1586 
1587 /* readonly attribute DOMString crop; */
1588 NS_IMETHODIMP sbMetadataJob::GetCrop(nsAString & aCrop)
1589 {
1590  TRACE(("%s[%.8x]", __FUNCTION__, this));
1591  aCrop.AssignLiteral("center");
1592  return NS_OK;
1593 }
1594 
1595 // =======================================
1596 // == sbIJobCancelable Implementation ==
1597 // =======================================
1598 
1599 /* boolean canCancel; */
1600 NS_IMETHODIMP sbMetadataJob::GetCanCancel(PRBool* _retval)
1601 {
1602  TRACE(("%s[%.8x]", __FUNCTION__, this));
1603  *_retval = PR_TRUE;
1604  return NS_OK;
1605 }
1606 
1607 /* void cancel(); */
1608 NS_IMETHODIMP sbMetadataJob::Cancel()
1609 {
1610  TRACE(("%s[%.8x]", __FUNCTION__, this));
1611  NS_ASSERTION(NS_IsMainThread(), \
1612  "sbMetadataJob::Cancel is main thread only!");
1613 
1614  // Cancel!
1615  // Don't worry about items that are off being processed.
1616  // Just clear out all work and raise a flag indicating
1617  // that we're done. The processing will finish and
1618  // the items will be thrown away.
1619 
1620  // Clear main thread items
1621  mMainThreadJobItems.Clear();
1622  mNextMainThreadIndex = 0;
1623 
1624  // Scope locks for background thread items
1625  {
1626  // Must lock in the same order as BatchCompleteItemsCallback.
1627  nsAutoLock processedLock(mProcessedBackgroundItemsLock);
1628  nsAutoLock waitingLock(mBackgroundItemsLock);
1629 
1630  // Set cancelled inside of the lock to make sure
1631  // there is no race between checking the cancelled flag
1632  // and accessing the arrays
1634 
1635  // Clear background thread items
1636  mBackgroundThreadJobItems.Clear();
1637  mNextBackgroundThreadIndex = 0;
1638 
1639  // Clear items that were processed in the background
1640  // and are waiting to be completed
1641  if (mProcessedBackgroundThreadItems) {
1642  mProcessedBackgroundThreadItems->Clear();
1643  }
1644  }
1645 
1646  // Let everyone else know
1647  OnJobProgress();
1648 
1649  // The sbFileMetadataService will notice that we are complete
1650  // and remove us from its list.
1651  return NS_OK;
1652 }
1653 
1654 
1655 // =========================
1656 // == Utility Functions ==
1657 // =========================
1658 
1659 nsresult sbMetadataJob::CreateDefaultItemName(sbIMediaItem* aItem,
1660  nsAString& retval)
1661 {
1662  TRACE(("%s[%.8x]", __FUNCTION__, this));
1663  NS_ASSERTION(NS_IsMainThread(),
1664  "sbMetadataJob::CreateDefaultItemName is main thread only!");
1665  NS_ENSURE_ARG_POINTER(aItem);
1666  nsresult rv;
1667 
1668  // Get the content URL
1669  nsCOMPtr<nsIURI> uri;
1670  rv = aItem->GetContentSrc(getter_AddRefs(uri));
1671  NS_ENSURE_SUCCESS(rv, rv);
1672 
1673  nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri, &rv);
1674  nsString filename;
1675  if (NS_SUCCEEDED(rv) && fileUrl) {
1676  nsCOMPtr<nsIFile> sourceFile, canonicalFile;
1677  rv = fileUrl->GetFile(getter_AddRefs(sourceFile));
1678  NS_ENSURE_SUCCESS(rv, rv);
1679 
1680  nsCOMPtr<sbILibraryUtils> libraryUtils =
1681  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
1682  NS_ENSURE_SUCCESS(rv, rv);
1683 
1684  rv = libraryUtils->GetCanonicalPath(sourceFile,
1685  getter_AddRefs(canonicalFile));
1686  NS_ENSURE_SUCCESS(rv, rv);
1687 
1688  rv = canonicalFile->GetLeafName(filename);
1689  NS_ENSURE_SUCCESS(rv, rv);
1690  }
1691  else {
1692  nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
1693  NS_ENSURE_SUCCESS(rv, rv);
1694 
1695  // Get the filename as UTF8
1696  nsCAutoString escapedURI, unescapedURI;
1697  rv = url->GetFileName(escapedURI);
1698  NS_ENSURE_SUCCESS(rv, rv);
1699 
1700  // Now unescape it
1701  nsCOMPtr<nsINetUtil> netUtil =
1702  do_GetService("@mozilla.org/network/util;1", &rv);
1703  NS_ENSURE_SUCCESS(rv, rv);
1704  rv = netUtil->UnescapeString(escapedURI,
1705  nsINetUtil::ESCAPE_ALL,
1706  unescapedURI);
1707  NS_ENSURE_SUCCESS(rv, rv);
1708 
1709  filename = NS_ConvertUTF8toUTF16(unescapedURI);
1710  }
1711 
1712  PRInt32 dot = filename.RFind(NS_LITERAL_STRING("."));
1713  if (dot > 0 && dot < (PRInt32)(filename.Length() - 1)) {
1714  retval = nsDependentSubstring(filename, 0, dot);
1715  }
1716  else {
1717  retval = filename;
1718  }
1719 
1720  return NS_OK;
1721 }
1722 
1723 
1724 nsresult
1725 sbMetadataJob::AppendToPropertiesIfValid(sbIPropertyManager* aPropertyManager,
1726  sbIMutablePropertyArray* aProperties,
1727  const nsAString& aID,
1728  const nsAString& aValue)
1729 {
1730  TRACE(("%s[%.8x]", __FUNCTION__, this));
1731  NS_ASSERTION(aPropertyManager, "aPropertyManager is null");
1732  NS_ASSERTION(aProperties, "aProperties is null");
1733 
1734  nsCOMPtr<sbIPropertyInfo> info;
1735  nsresult rv = aPropertyManager->GetPropertyInfo(aID, getter_AddRefs(info));
1736  NS_ENSURE_SUCCESS(rv, rv);
1737 
1738  PRBool isValid = PR_FALSE;
1739  rv = info->Validate(aValue, &isValid);
1740  NS_ENSURE_SUCCESS(rv, rv);
1741 
1742  if (isValid) {
1743  rv = aProperties->AppendProperty(aID, aValue);
1744  NS_ENSURE_SUCCESS(rv, rv);
1745  }
1746 
1747  return NS_OK;
1748 }
1749 
1750 
1751 nsresult
1752 sbMetadataJob::GetFileSize(sbIMediaItem* aMediaItem, PRInt64* aFileSize)
1753 {
1754  TRACE(("%s[%.8x]", __FUNCTION__, this));
1755  NS_ENSURE_ARG_POINTER(aMediaItem);
1756  NS_ENSURE_ARG_POINTER(aFileSize);
1757  NS_ASSERTION(NS_IsMainThread(), "GetFileSize is main thread only!");
1758 
1759  nsresult rv;
1760  nsCOMPtr<nsIURI> uri;
1761  rv = aMediaItem->GetContentSrc(getter_AddRefs(uri));
1762  NS_ENSURE_SUCCESS(rv, rv);
1763 
1764  // If this is not a file url, just return the error
1765  nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri, &rv);
1766  if (rv == NS_ERROR_NO_INTERFACE) {
1767  // If this isn't a local file and we can't get the filesize
1768  // that's fine. Don't NS_WARN about it.
1769  return rv;
1770  }
1771  NS_ENSURE_SUCCESS(rv, rv);
1772 
1773  nsCOMPtr<nsIFile> file;
1774  rv = fileUrl->GetFile(getter_AddRefs(file));
1775  NS_ENSURE_SUCCESS(rv, rv);
1776 
1777  return file->GetFileSize(aFileSize);;
1778 }
1779 
1780 
1781 nsresult
1782 sbMetadataJob::LocalizeString(const nsAString& aName, nsAString& aValue)
1783 {
1784  TRACE(("%s[%.8x] (%s)", __FUNCTION__, this,
1785  NS_LossyConvertUTF16toASCII(aName).get()));
1786  nsresult rv = NS_OK;
1787 
1788  if (!mStringBundle) {
1789  nsCOMPtr<nsIStringBundleService> StringBundleService =
1790  do_GetService("@mozilla.org/intl/stringbundle;1", &rv );
1791  NS_ENSURE_SUCCESS(rv, rv);
1792 
1793  rv = StringBundleService->CreateBundle(
1794  "chrome://songbird/locale/songbird.properties",
1795  getter_AddRefs(mStringBundle));
1796  NS_ENSURE_SUCCESS(rv, rv);
1797  }
1798  nsString name(aName);
1799  nsString value;
1800  rv = mStringBundle->GetStringFromName(name.get(),
1801  getter_Copies(value));
1802  if (NS_FAILED(rv)) {
1803  NS_ERROR("sbMetadataJob::LocalizeString failed to find a string");
1804  }
1805  aValue = value;
1806  return rv;
1807 }
nsresult SetProcessingStarted(PRBool aProcessingStarted)
#define TRACE(args)
nsresult SetURL(const nsACString &aURL)
nsresult OnJobProgress()
return NS_OK
const PRUint32 NUM_ITEMS_PROCESSED_THRESHOLD
Interface to control UI aspects of sbIJobProgress.
#define SB_PROPERTY_ORIGINURL
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
function succeeded(ch, cx, status, data)
Generic interface for exposing long running jobs to the UI.
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
#define SB_PROPERTY_RATING
nsresult GetType(JobType *aJobType)
nsresult AppendMediaItems(nsIArray *aMediaItemsArray)
NS_IMPL_THREADSAFE_CI(sbMediaListEnumeratorWrapper)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
nsresult GetURL(nsACString &aURL)
friend class sbMediaListBatchCallback
Definition: sbMetadataJob.h:85
nsresult GetQueuedItem(PRBool aMainThreadOnly, sbMetadataJobItem **aJobItem)
virtual ~sbMetadataJob()
#define SB_PROPERTYMANAGER_CONTRACTID
#define SB_PROPERTY_CONTENTLENGTH
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
An interface to carry around arrays of nsIProperty instances Note that implementations of the interfa...
const unsigned long TYPE_LOCAL
A container of sbMetadataJobItems.
A component which is interested in the result of an album art fetch request.
nsresult GetProcessed(PRBool *aProcessed)
#define SB_PROPERTY_CONTENTTYPE
BogusChannel prototype contentLength
nsresult GetMediaItem(sbIMediaItem **aMediaItem)
nsresult PutProcessedItem(sbMetadataJobItem *aJobItem)
nsresult SetBlocked(PRBool aBlocked)
const unsigned short MAIN_THREAD_PROCESSOR
Main Thread Processor Flag for restartProcessors.
NS_IMPL_THREADSAFE_ISUPPORTS5(sbMetadataJob, nsIClassInfo, sbIJobProgress, sbIJobProgressUI, sbIJobCancelable, sbIAlbumArtListener)
Songbird String Bundle Definitions.
sbStringSet::iterator sbStringSetIter
nsresult Init(nsIArray *aMediaItemsArray, nsIStringEnumerator *aRequiredProperties, JobType aJobType)
sbStringSet::iterator sbStringSetIter
nsresult AppendJobItem(sbMetadataJobItem *aJobItem)
_updateCookies aName
const unsigned short BACKGROUND_THREAD_PROCESSOR
Background Thread Processor Flag for restartProcessors.
function url(spec)
var uri
Definition: FeedWriter.js:1135
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
nsresult SetHandler(sbIMetadataHandler *aHandler)
sbIJobCancelable NS_DECL_CLASSINFO(sbGstreamerMediaInspector)
static void AppendInt(nsAString &str, PRInt64 val)
NS_IMPL_CI_INTERFACE_GETTER5(sbMetadataJob, nsIClassInfo, sbIJobProgress, sbIJobProgressUI, sbIJobCancelable, sbIAlbumArtListener) sbMetadataJob
Implemented to receive notifications from sbIJobProgress interfaces.
Interface that defines a single item of media in the system.
#define SB_PROPERTY_TRACKNAME
restoreHistoryPrecursor aCount
nsresult SetProcessed(PRBool aProcessed)
const PRUint32 NUM_BACKGROUND_ITEMS_BEFORE_FLUSH
Interface for an album art fetcher. Instantiate as a component instance.
A metadata job task.
#define SB_PROPERTY_CONTENTURL
const unsigned short STATUS_FAILED
Constant indicating that the job has completed with errors.
nsresult GetProperties(sbIMutablePropertyArray **aPropertyArray)
_getSelectedPageStyle s i
#define SB_PROPERTY_PRIMARYIMAGEURL
Manager for system wide metadata properties.
GstMessage gpointer data sbGStreamerMessageHandler * handler
#define LOG(args)
nsresult GetHandler(sbIMetadataHandler **aHandler)
function next()
var file